trytonpsk-sale_pos_frontend.../sale.py

425 lines
16 KiB
Python
Raw Normal View History

2020-04-15 21:49:23 +02:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from __future__ import unicode_literals
2021-01-21 20:35:43 +01:00
from datetime import date, datetime
from sql import Table
2020-04-15 21:49:23 +02:00
from trytond.pool import PoolMeta, Pool
from trytond.model import fields, ModelSQL
2020-04-15 21:49:23 +02:00
from trytond.pyson import Eval
from trytond.transaction import Transaction
2020-07-22 06:26:28 +02:00
from trytond.wizard import Wizard
from decimal import Decimal
2021-09-17 00:34:17 +02:00
from trytond.wizard import StateReport
from trytond.report import Report
2021-07-23 00:35:39 +02:00
from trytond.i18n import gettext
from .exceptions import PayValidationError, ProductionValidationWarning
2021-09-17 00:34:17 +02:00
from trytond.modules.sale_pos_frontend.sale import SaleSquareBox
2021-09-18 18:03:55 +02:00
from operator import itemgetter
2020-04-15 21:49:23 +02:00
2020-12-20 12:20:38 +01:00
KIND = [
2020-07-22 06:26:28 +02:00
('', ''),
('take_away', 'Take Away'),
('delivery', 'Delivery'),
2020-12-20 12:20:38 +01:00
('to_table', 'To Table')
2020-07-22 06:26:28 +02:00
]
2020-04-15 21:49:23 +02:00
class SaleMove(ModelSQL):
"Sale - Stock Move"
__name__ = "sale.sale-stock.move"
_table = 'sale_sale-stock_move_rel'
sale = fields.Many2One('sale.sale', 'Sale',
ondelete='CASCADE', select=True, required=True)
move = fields.Many2One('stock.move', 'Stock Move',
ondelete='CASCADE', select=True, required=True)
2020-04-15 21:49:23 +02:00
class Sale(metaclass=PoolMeta):
__name__ = 'sale.sale'
2020-12-20 12:20:38 +01:00
consumer = fields.Many2One('party.consumer', 'Consumer')
2020-04-15 21:49:23 +02:00
table_assigned = fields.Many2One('sale.shop.table', 'Table Assigned',
domain=[
2021-01-21 20:35:43 +01:00
('shop', '=', Eval('shop')),
2020-04-15 21:49:23 +02:00
])
productions = fields.Function(fields.One2Many('production', None, 'Productions'), 'get_productions')
production_moves = fields.Many2Many('sale.sale-stock.move', 'sale', 'move', 'Production Moves')
2020-07-22 06:26:28 +02:00
delivery_time = fields.Numeric('Delivery Time (Min)', help="In Minutes")
2021-01-21 20:35:43 +01:00
order_status = fields.Selection([
('', ''),
('draft', 'Draft'),
('commanded', 'Commanded'),
('in_preparation', 'In Preparation'),
('dispatched', 'Dispatched'),
('delivered', 'Delivered'),
('rejected', 'Rejected'),
('cancelled', 'Cancelled'),
], 'Order Status')
order_status_string = order_status.translated('order_status')
shipping_time = fields.DateTime('Shipping Time')
2021-01-18 05:50:11 +01:00
waiting_time = fields.Function(fields.Integer('Shipping Time',
help="In Minutes"), 'get_waiting_time')
@classmethod
def __setup__(cls):
super(Sale, cls).__setup__()
2021-01-21 20:35:43 +01:00
@staticmethod
def default_order_status():
return 'draft'
2021-01-18 05:50:11 +01:00
def get_waiting_time(self, name=None):
2021-01-21 20:35:43 +01:00
now = datetime.now()
2021-01-18 05:50:11 +01:00
if self.state in ('draft', 'quotation', 'confirmed', 'processing'):
2021-01-21 20:35:43 +01:00
delta = int((now - self.create_date).seconds / 60)
2021-01-18 05:50:11 +01:00
return delta
2020-12-31 18:32:38 +01:00
2020-06-12 03:51:21 +02:00
@classmethod
def transition_pay_(cls, sales_to_pay):
pool = Pool()
Statement = pool.get('account.statement')
StatementLine = pool.get('account.statement.line')
for sale_pay in sales_to_pay:
sale = sale_pay['sale']
journal_id = sale_pay['journal_id']
statements = Statement.search([
('journal', '=', journal_id),
('state', '=', 'draft'),
('sale_device', '=', sale.sale_device),
], order=[('date', 'DESC')])
if not statements:
2021-07-23 00:35:39 +02:00
raise PayValidationError(
gettext('sale_pos_frontend_rest.msg_message_error', s='A draft statement payments has not been created.'))
2020-06-12 03:51:21 +02:00
if not sale.number:
cls.set_number([sale])
2021-07-23 00:35:39 +02:00
if not sale.party.account_receivable:
raise PayValidationError(
gettext('sale_pos_frontend_rest.msg_party_not_account_receivable', s=sale.party.name))
account = sale.party.account_receivable.id
2020-06-12 03:51:21 +02:00
payment = StatementLine(
statement=statements[0].id,
date=date.today(),
amount=sale.total_amount,
party=sale.party.id,
account=account,
description=sale.number,
sale=sale.id,
)
payment.save()
sale.save()
cls.workflow_to_end([sale])
2020-07-22 06:26:28 +02:00
@classmethod
def update_consumer(cls, args, context):
Consumer = Pool().get('party.consumer')
fields = args['fields']
2020-12-20 22:52:38 +01:00
consumer = Consumer(args['id'])
2020-07-22 06:26:28 +02:00
for key, value in fields.items():
2020-09-03 06:04:14 +02:00
if hasattr(consumer, key):
Consumer.write([consumer], {key: value})
2020-08-20 06:42:07 +02:00
return cls._get_object(consumer)
2020-07-22 06:26:28 +02:00
@classmethod
def create_consumer(cls, args, context):
Consumer = Pool().get('party.consumer')
consumer = None
2020-12-21 15:23:11 +01:00
notes = ''
2021-08-20 02:48:24 +02:00
if args:
# fields = args['fields']
print('args....', args)
consumer, = Consumer.create([args])
2021-08-20 02:36:26 +02:00
# if consumer and consumer.party:
# party = consumer.party
# party.write([party], {'notes': notes})
# return cls._get_object(consumer)
2020-12-20 22:52:38 +01:00
return {
2021-08-20 02:48:24 +02:00
'id': consumer.id,
'name': consumer.name,
'phone': consumer.phone,
'address': consumer.address or '',
'notes': consumer.notes,
2022-02-03 13:45:34 +01:00
'delivery': consumer.delivery
2020-12-20 22:52:38 +01:00
}
2020-07-22 06:26:28 +02:00
2020-08-20 06:42:07 +02:00
@classmethod
def _get_object(cls, consumer):
obj_ = {
2020-12-20 22:52:38 +01:00
'msg': 'ok',
2021-08-20 02:36:26 +02:00
# 'party': None,
2020-12-20 22:52:38 +01:00
'consumer': {
'id': consumer.id,
'name': consumer.name,
'phone': consumer.phone,
2020-12-27 02:25:04 +01:00
'address': consumer.address or '',
'notes': consumer.notes or '',
2020-12-20 22:52:38 +01:00
},
}
2020-08-20 06:42:07 +02:00
if consumer.party:
obj_['party'] = consumer.party.id
return obj_
@classmethod
def _move(cls, from_location, to_location, company, product, uom,
quantity):
Move = Pool().get('stock.move')
move = Move(
product=product,
uom=uom,
quantity=quantity,
from_location=from_location,
to_location=to_location,
company=company,
currency=company.currency if company else None,
state='draft',
)
return move
@classmethod
def _explode_move_values(cls, from_location, to_location, company,
bom_io, quantity):
move = cls._move(from_location, to_location, company,
bom_io.product, bom_io.uom, quantity)
move.from_location = from_location.id if from_location else None
move.to_location = to_location.id if to_location else None
move.unit_price_required = move.on_change_with_unit_price_required()
return move
@classmethod
def _create_productions(cls, lines):
pool = Pool()
Move = Pool().get('stock.move')
Location = pool.get('stock.location')
Bom = pool.get('production.bom')
Uom = pool.get('product.uom')
for line in lines:
2021-04-15 22:22:23 +02:00
if hasattr(line, 'production') and line.production:
return
if line.product.producible:
boms = Bom.search([
('output_products', '=', line.product.id)
])
if not boms:
2020-06-12 03:51:21 +02:00
continue
2021-07-23 00:35:39 +02:00
raise ProductionValidationWarning(
gettext('sale_pos_frontend_rest.msg_msg_product_without_bom', product=line.product.rec_name))
else:
bom_ = boms[0]
locations = Location.search([
('type', '=', 'production'),
])
location_ = locations[0] if locations else None
2021-04-15 22:34:30 +02:00
if not hasattr(line, 'sale'):
2021-04-15 22:31:59 +02:00
return
sale = line.sale
date_ = sale.sale_date
product_ = line.product
if not (bom_ and product_ and product_.default_uom):
return
if sale.warehouse:
storage_location = sale.warehouse.storage_location
else:
storage_location = None
factor = bom_.compute_factor(product_, line.quantity or 0,
product_.default_uom)
inputs = []
for input_ in bom_.inputs:
quantity = input_.compute_quantity(factor)
move = cls._explode_move_values(storage_location,
location_, sale.company, input_, quantity)
if move:
move.planned_date = date_
move.effective_date = date_
move.save()
inputs.append(move)
quantity = Uom.compute_qty(input_.uom, line.quantity,
input_.product.default_uom, round=False)
outputs = []
for output in bom_.outputs:
quantity = output.compute_quantity(factor)
move = cls._explode_move_values(location_, storage_location,
sale.company, output, quantity)
if move:
move.planned_date = date_
move.effective_date = date_
move.unit_price = Decimal(0)
move.save()
outputs.append(move)
moves = inputs + outputs
Move.do(inputs+outputs)
sale.production_moves = moves
sale.save()
def get_productions(self, name):
productions = []
for line in self.lines:
2021-04-15 22:22:23 +02:00
if hasattr(line, 'production') and line.production:
productions.append(line.production)
return productions
class SaleForceDraft(Wizard):
__name__ = 'sale_pos.force_draft'
def transition_force_draft(self):
pool = Pool()
Sale = pool.get('sale.sale')
Production = pool.get('production')
ids = Transaction().context['active_ids']
stock_move_table = Table('stock_move')
cursor = Transaction().connection.cursor()
if not ids:
return 'end'
for sale in Sale.browse(ids):
stock_moves = [line.id for line in sale.production_moves]
if stock_moves:
cursor.execute(*stock_move_table.update(
columns=[stock_move_table.state],
values=['draft'],
where=stock_move_table.id.in_(stock_moves)
))
cursor.execute(*stock_move_table.delete(
where=stock_move_table.id.in_(stock_moves))
)
for p in sale.productions:
cursor = Transaction().connection.cursor()
cursor.execute("UPDATE production SET state='waiting' WHERE id in (%s)" % (p.id))
Production.draft([p])
Production.cancel([p])
Production.delete([p])
return super(SaleForceDraft, self).transition_force_draft()
class SaleLine(metaclass=PoolMeta):
__name__ = 'sale.line'
production = fields.Many2One('production', 'Production')
2020-04-15 21:49:23 +02:00
class Shop(metaclass=PoolMeta):
__name__ = 'sale.shop'
2020-12-20 12:20:38 +01:00
tables = fields.One2Many('sale.shop.table', 'shop', 'Tables')
2021-09-17 00:34:17 +02:00
class SaleSquareBoxGlobal(SaleSquareBox):
'Sale Square Box Global'
__name__ = 'sale_pos_frontend_rest.sale_square_box_global'
@classmethod
def __setup__(cls):
super(SaleSquareBoxGlobal, cls).__setup__()
cls.print_ = StateReport('sale_pos_frontend_rest.sale_square_box_global_report')
class SaleSquareBoxGlobalReport(Report):
'Square Box Global Report'
__name__ = 'sale_pos_frontend_rest.sale_square_box_global_report'
@classmethod
def __setup__(cls):
super(SaleSquareBoxGlobalReport, cls).__setup__()
@classmethod
def get_stament_cash(cls, statement):
Statement = Pool().get('account.statement')
if statement:
value = {
'statement': Statement(statement['id']),
'base': statement['total_money'] or 0,
'bills': sum(k['amount'] for k in statement['count_money.'] if float(k['bill']) >= 1000),
'coins': sum(k['amount'] for k in statement['count_money.'] if float(k['bill']) < 1000)
}
return value
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Invoice = pool.get('account.invoice')
Company = pool.get('company.company')
Device = pool.get('sale.device')
company = Company(data['company'])
Statement = pool.get('account.statement')
Shop = pool.get('sale.shop')
# User = pool.get('res.user')
dom_statement = [
('date', '=', data['date']),
]
devices = Device.search([('shop', '=', data['shop'])])
device_ids = [d.id for d in devices]
dom_statement.append(('sale_device', 'in', device_ids))
if data['turn']:
dom_statement.append(('turn', '=', int(data['turn'])))
fields_statement = ['name', 'create_date', 'journal.kind', 'balance', 'count_money.bill', 'count_money.quantity', 'count_money.amount', 'total_money']
cash = None
statements = Statement.search_read(dom_statement, fields_names=fields_statement)
lst_statements = []
balance_statements = []
for st in statements:
kind = st['journal.']['kind']
if kind == 'cash':
cash = st
else:
balance_statements.append(st['balance'])
lst_statements.append(st)
dom_invoices = [
('company', '=', data['company']),
('invoice_date', '=', data['date']),
('type', '=', 'out'),
('shop', '=', data['shop']),
('turn', '=', int(data['turn']))
]
2021-09-18 18:03:55 +02:00
fields_inv = ['number', 'invoice_type', 'total_amount', 'state',
'payment_term.payment_type', 'sales.residual_amount',
'payment_term.rec_name', 'party.name', 'reference']
2021-09-17 00:34:17 +02:00
invoices = Invoice.search_read(dom_invoices, fields_names=fields_inv)
2021-09-18 18:03:55 +02:00
pos = []
electronic = []
cancelled = []
2021-09-17 00:34:17 +02:00
credits = []
2021-09-18 18:03:55 +02:00
invs = []
pos_append = pos.append
electronic_append = electronic.append
cancelled_append = cancelled.append
credits_append = credits.append
invs_append = invs.append
2021-09-17 00:34:17 +02:00
for invoice in invoices:
2021-09-18 18:03:55 +02:00
residual_amount = invoice['sales.'][0]['residual_amount']
if residual_amount > 0:
invoice['residual_amount'] = residual_amount
credits_append(residual_amount)
invs_append(invoice)
2021-09-17 00:34:17 +02:00
if invoice['state'] == 'cancelled':
2021-09-18 18:03:55 +02:00
cancelled_append((invoice['total_amount'], invoice))
2021-09-17 00:34:17 +02:00
elif invoice['invoice_type'] == '1':
2021-09-18 18:03:55 +02:00
electronic_append(invoice['total_amount'])
2021-09-17 00:34:17 +02:00
else:
2021-09-18 18:03:55 +02:00
pos_append(invoice['total_amount'])
2021-09-17 00:34:17 +02:00
2021-09-18 18:03:55 +02:00
report_context['total_sale'] = sum(pos) + sum(electronic)
report_context['pos'] = sum(pos)
report_context['electronic'] = sum(electronic)
2021-09-17 00:34:17 +02:00
report_context['cash'] = cls.get_stament_cash(cash)
report_context['credits'] = sum(credits)
2021-09-18 18:03:55 +02:00
report_context['invoice_credits'] = invs
2021-09-17 00:34:17 +02:00
report_context['statements'] = lst_statements
report_context['balance_statements'] = balance_statements
report_context['data'] = data
report_context['company'] = company
report_context['shop'] = Shop(data['shop'])
return report_context