trytonpsk-sale_pos_frontend.../sale.py
wilson gomez sanchez 7eb95d6463 minor fix
2021-04-15 15:22:23 -05:00

312 lines
11 KiB
Python

# 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
from datetime import date, datetime
from sql import Table
from trytond.pool import PoolMeta, Pool
from trytond.model import fields, ModelSQL
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.wizard import Wizard
from decimal import Decimal
__all__ = ['Sale', 'Shop', 'SaleMove', 'SaleForceDraft']
KIND = [
('', ''),
('take_away', 'Take Away'),
('delivery', 'Delivery'),
('to_table', 'To Table')
]
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)
class Sale(metaclass=PoolMeta):
__name__ = 'sale.sale'
consumer = fields.Many2One('party.consumer', 'Consumer')
table_assigned = fields.Many2One('sale.shop.table', 'Table Assigned',
domain=[
('shop', '=', Eval('shop')),
])
productions = fields.Function(fields.One2Many('production', None, 'Productions'), 'get_productions')
production_moves = fields.Many2Many('sale.sale-stock.move', 'sale', 'move', 'Production Moves')
delivery_time = fields.Numeric('Delivery Time (Min)', help="In Minutes")
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')
waiting_time = fields.Function(fields.Integer('Shipping Time',
help="In Minutes"), 'get_waiting_time')
@classmethod
def __setup__(cls):
super(Sale, cls).__setup__()
cls._error_messages.update({
'product_without_bom': (
"The product '%(product)s' has no production bom"),
'message_error': 'Error: %s',
},)
@staticmethod
def default_order_status():
return 'draft'
def get_waiting_time(self, name=None):
now = datetime.now()
if self.state in ('draft', 'quotation', 'confirmed', 'processing'):
delta = int((now - self.create_date).seconds / 60)
return delta
@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:
cls.raise_user_error(
'message_error',
'A draft statement payments has not been created.'
)
if not sale.number:
cls.set_number([sale])
account = (sale.party.account_receivable and sale.party.account_receivable.id
or cls.raise_user_error('message_error', 'Party %s has no any \
account receivable defined. Please, assign one.' % (sale.party.name)))
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])
@classmethod
def update_consumer(cls, args, context):
Consumer = Pool().get('party.consumer')
fields = args['fields']
consumer = Consumer(args['id'])
for key, value in fields.items():
if hasattr(consumer, key):
Consumer.write([consumer], {key: value})
return cls._get_object(consumer)
@classmethod
def create_consumer(cls, args, context):
Consumer = Pool().get('party.consumer')
consumer = None
notes = ''
if args.get('fields'):
fields = args['fields']
consumer, = Consumer.create([fields])
if consumer and consumer.party:
party = consumer.party
party.write([party], {'notes': notes})
return cls._get_object(consumer)
return {
'msg': 'ok',
'consumer': {
'id': consumer.id,
'name': consumer.name,
'phone': consumer.phone,
'address': consumer.address or '',
'notes': consumer.notes,
}
}
@classmethod
def _get_object(cls, consumer):
obj_ = {
'msg': 'ok',
'party': None,
'consumer': {
'id': consumer.id,
'name': consumer.name,
'phone': consumer.phone,
'address': consumer.address or '',
'notes': consumer.notes or '',
},
}
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:
if hasattr(line, 'production') and line.production:
return
if line.product.producible:
boms = Bom.search([
('output_products', '=', line.product.id)
])
if not boms:
continue
warning_name = '%s.product.without_bom' % line
cls.raise_user_warning(
warning_name, 'product_without_bom', {
'product': line.product.rec_name,
})
else:
bom_ = boms[0]
locations = Location.search([
('type', '=', 'production'),
])
location_ = locations[0] if locations else None
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:
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')
class Shop(metaclass=PoolMeta):
__name__ = 'sale.shop'
tables = fields.One2Many('sale.shop.table', 'shop', 'Tables')