trytonpsk-sale_pos/sale.py

1954 lines
72 KiB
Python
Raw Normal View History

2020-04-15 21:47:31 +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.
2020-12-20 20:57:25 +01:00
import math
2020-04-15 21:47:31 +02:00
from decimal import Decimal
from datetime import datetime, date
from itertools import chain
from sql import Table
from trytond.model import ModelView, fields
from trytond.pool import PoolMeta, Pool
from trytond.transaction import Transaction
2021-02-09 17:17:01 +01:00
from trytond.pyson import Bool, Eval, Not
2020-04-15 21:47:31 +02:00
from trytond.wizard import (
2020-12-26 15:12:46 +01:00
Wizard, StateView, StateAction, StateReport, StateTransition, Button
)
2020-04-15 21:47:31 +02:00
from trytond.report import Report
2021-06-22 20:38:42 +02:00
from trytond.i18n import gettext
from .exceptions import (ProductMissingTaxError, ImportSalesError,
SaleDeleteError, SaleForceDraftError, SaleDeviceError, DraftStatementError,
PartyMissingAccount)
from trytond.modules.sale.exceptions import SaleValidationError
2020-04-15 21:47:31 +02:00
_ZERO = Decimal('0.00')
TODAY = date.today()
STATES = {
'readonly': Eval('state').in_(['processing', 'done', 'cancel']),
}
class Sale(metaclass=PoolMeta):
__name__ = 'sale.sale'
self_pick_up = fields.Boolean('Self Pick Up', states=STATES, depends=['state'],
help='The goods are picked up by the customer before the sale, so no '
'shipment is created.')
pos_create_date = fields.DateTime('Create Date', readonly=True)
payments = fields.One2Many('account.statement.line', 'sale', 'Payments')
2020-12-09 04:53:04 +01:00
paid_amount = fields.Function(fields.Numeric('Paid Amount', digits=(16, 2)),
2020-04-15 21:47:31 +02:00
'get_paid_amount')
invoice_number = fields.Char('Invoice Number')
invoice_date = fields.Date('Invoice Date')
invoice = fields.Many2One('account.invoice', 'Invoice')
2021-02-09 17:17:01 +01:00
residual_amount = fields.Function(fields.Numeric('Residual Amount',
2021-02-24 04:04:13 +01:00
digits=(16, 2), readonly=True), 'get_residual_amount')
2020-04-15 21:47:31 +02:00
sale_device = fields.Many2One('sale.device', 'Sale Device',
2021-02-24 04:04:13 +01:00
domain=[('shop', '=', Eval('shop'))], depends=['shop'], states=STATES)
2020-04-15 21:47:31 +02:00
position = fields.Char('Position', states=STATES)
pre_sale = fields.Boolean('Pre-Sale', help="This option for pre-sale, change method of invoice to shipment")
shipment_date = fields.Date('Shipment Date', states=STATES)
2021-01-04 14:11:58 +01:00
turn = fields.Integer('Turn', states=STATES)
2020-04-15 21:47:31 +02:00
@classmethod
def __setup__(cls):
super(Sale, cls).__setup__()
cls.party.states['readonly'] = Bool(Eval('invoice_number'))
cls.state_string = super(Sale, cls).state.translated('state')
2021-02-23 18:40:48 +01:00
for fname in cls.self_pick_up.on_change:
if fname not in cls.shop.on_change:
cls.shop.on_change.add(fname)
if fname not in cls.party.on_change:
cls.party.on_change.add(fname)
2020-04-15 21:47:31 +02:00
cls._buttons.update({
'wizard_sale_payment': {
'invisible': Eval('state') == 'done',
'readonly': Not(Bool(Eval('lines'))),
},
'wizard_generate_invoice': {
'invisible': Eval('state').in_(['done', 'processing', 'cancel']),
'readonly': Not(Bool(Eval('lines'))),
},
'wizard_generate_shipment': {
'invisible': Eval('shipment_state').in_(['sent', 'exception']),
'readonly': Not(Bool(Eval('lines'))),
},
})
@classmethod
def search_rec_name(cls, name, clause):
_, operator, value = clause
if operator.startswith('!') or operator.startswith('not '):
bool_op = 'AND'
else:
bool_op = 'OR'
domain = [
bool_op,
('number', operator, value),
('reference', operator, value),
('description', operator, value),
('invoice.number', operator, value),
]
return domain
@staticmethod
def default_sale_device():
User = Pool().get('res.user')
user = User(Transaction().user)
return user.sale_device and user.sale_device.id or None
@classmethod
def process(cls, sales):
for sale in sales:
2021-02-09 17:17:01 +01:00
cls.recheck_taxes(sale)
2020-04-15 21:47:31 +02:00
cls.process_pos(sale)
super(Sale, cls).process(sales)
2021-02-09 17:17:01 +01:00
@classmethod
def recheck_taxes(cls, sale):
for line in sale.lines:
vat_required = None
2021-02-16 23:54:42 +01:00
if line.product and line.product.account_category:
2021-02-09 17:17:01 +01:00
for txr in line.product.account_category.customer_taxes_used:
if txr.type == 'percentage' and txr.rate > 0:
vat_required = txr.id
break
2021-02-16 23:54:42 +01:00
if vat_required:
tax_obj = [t.id for t in line.taxes if t.rate and t.rate > 0]
if vat_required not in tax_obj:
2021-06-22 20:38:42 +02:00
raise ProductMissingTaxError(
gettext('sale_pos.msg_missing_tax', s=line.product.rec_name))
2021-01-14 01:21:41 +01:00
2020-12-09 04:53:04 +01:00
def get_tax_amount(self):
# Esto es necesario para impuestos de Licores y Cigarrillos
values = self._get_taxes().values()
res = sum((v['amount'] for v in values), Decimal(0))
Tax = Pool().get('account.tax')
taxes_update = Tax.search([
('type', '=', 'fixed'),
('group.kind', '=', 'sale'),
('amount', '=', 0),
])
taxes_update_ids = [t.id for t in taxes_update]
taxes_update_id = None
if taxes_update_ids:
taxes_update_id = taxes_update_ids[0]
amounts = []
if taxes_update_id:
for l in self.lines:
2020-12-10 14:37:54 +01:00
if not l.product or (not hasattr(l.product, 'extra_tax') or not l.product.extra_tax):
2020-12-09 04:53:04 +01:00
continue
2021-01-13 18:29:40 +01:00
raw_val = Decimal(float(l.product.extra_tax) * l.quantity)
val = self.currency.round(raw_val)
2020-12-09 04:53:04 +01:00
amounts.append(val)
return res + sum(amounts)
2021-03-31 21:33:38 +02:00
@classmethod
def import_data(cls, fields_names, data):
pool = Pool()
Product = pool.get('product.product')
Party = pool.get('party.party')
Invoice = pool.get('account.invoice')
SaleLine = pool.get('sale.line')
Voucher = pool.get('account.voucher')
PaymentMode = pool.get('account.voucher.paymode')
Tax = pool.get('account.tax')
user_ = pool.get('res.user')(Transaction().user)
shop_id = user_.shop.id
device_id = user_.sale_device.id
count = 0
# number_sales = list(set([v[0] for v in data[1:]]))
code_products = list(set([v[6] for v in data[1:]]))
number_parties = list(set([v[4] for v in data[1:]]))
products = Product.search([
('code', 'in', code_products),
('salable', '=', 'true')
])
products = [p.code for p in products]
not_product = [code for code in code_products if code not in products]
if not products or len(not_product) > 0:
not_product = code_products if not products else not_product
not_product = ', '.join(map(str, not_product))
2021-06-22 20:38:42 +02:00
raise ImportSalesError(
gettext('sale_pos.msg_import_data_sale', field='producto', s=not_product))
2021-03-31 21:33:38 +02:00
parties = Party.search([
('id_number', 'in', number_parties),
])
parties = [p.id_number for p in parties]
not_party = [party for party in number_parties if party not in parties]
if not parties or len(not_party) > 0:
not_party = number_parties if not parties else not_party
not_party = ', '.join(map(str, not_party))
2021-06-22 20:38:42 +02:00
raise ImportSalesError(
gettext('sale_pos.msg_import_data_sale', field='tercero', s=not_party))
2021-03-31 21:33:38 +02:00
sale_to_create = {}
sales_to_pay = []
for row in data[1:]:
code_ = row[6]
products = Product.search([
('code', '=', code_)
])
product = products[0]
day, month, year = row[3].split('/')
sale_date = date(int(year), int(month), int(day))
partys = Party.search([
('id_number', '=', row[4])
])
party = partys[0]
if row[0] not in sale_to_create.keys():
# with Transaction().set_context(ctx):
sale, = cls.create([{
'sale_date': sale_date,
'party': party.id,
'number': row[0],
'invoice_number': row[0],
'invoice_date': sale_date,
'invoice_type': 'P',
'description': row[1],
'reference': row[2],
'payment_term': 1,
'shop': shop_id,
'sale_device': device_id,
'invoice_address': party.address_get(type='invoice'),
'shipment_address': party.address_get(type='delivery'),
}])
sale.on_change_party()
sale.save()
sale_to_create[row[0]] = sale.id
payment_modes = PaymentMode.search([
('id', '=', row[10])
])
if not payment_modes:
2021-06-22 20:38:42 +02:00
not_party
raise ImportSalesError(
gettext('sale_pos.msg_import_data_sale', field='modo de pago', s=row[10]))
2021-03-31 21:33:38 +02:00
sales_to_pay.append({
'sale': sale,
'payment_mode': payment_modes[0]
})
count += 1
sale_id = sale_to_create[row[0]]
tax_imp = list(product.customer_taxes_used)
if row[9]:
tax = Tax.search(['id', '=', row[9]])
tax_imp.extend(tax)
line = {
'sale': sale_id,
'product': product.id,
'quantity': row[7],
'description': row[5],
'unit_digits': product.sale_uom.digits,
'unit': product.sale_uom,
'unit_price': Decimal(row[8]),
'discount': Decimal('0.00'),
'taxes': [('add', tax_imp)],
}
SaleLine.create([line])
for p in sales_to_pay:
sale = p['sale']
payment_mode = p['payment_mode']
cls.store_cache([sale])
sale = cls.process_pos(sale)
invoice = sale.invoice
invoice.accounting_date = sale.invoice_date
cls.post_invoices(sale)
sale.state = 'done'
sale.invoice_state = 'paid'
sale.save()
vouchers = Invoice.create_voucher([invoice])
for voucher in vouchers:
voucher.date = sale.invoice_date
voucher.payment_mode = payment_mode
voucher.save()
Voucher.process([voucher])
Voucher.post([voucher])
return count
2020-12-20 20:57:25 +01:00
def create_invoice(self):
'Create and return an invoice'
pool = Pool()
Invoice = pool.get('account.invoice')
if self.invoice_method == 'manual':
return
invoice_lines = []
sales = [self]
for sale in sales:
for line in sale.lines:
if self.total_amount < 0 and line.quantity > 0:
continue
if self.total_amount > 0 and line.quantity < 0:
continue
invoice_lines.append(line.get_invoice_line())
invoice_lines = list(chain(*invoice_lines))
if not invoice_lines:
return
2021-01-13 14:16:24 +01:00
sale = sales[0]
2020-12-20 20:57:25 +01:00
invoice = self._get_invoice_sale()
2021-01-13 14:16:24 +01:00
if self.invoice_type:
invoice.authorization = self._get_authorization(sale)
invoice.invoice_type = self.invoice_type
2020-12-20 20:57:25 +01:00
if getattr(invoice, 'lines', None):
invoice_lines = list(invoice.lines) + invoice_lines
invoice.lines = invoice_lines
2021-01-14 01:21:41 +01:00
invoice.turn = self._get_turn()
2020-12-20 20:57:25 +01:00
invoice.save()
Invoice.update_taxes([invoice])
return invoice
2021-01-13 14:16:24 +01:00
def _get_authorization(self, sale):
authorization_id = None
if sale.untaxed_amount_cache >= 0:
2021-01-13 14:16:24 +01:00
if sale.invoice_type == 'P' and sale.shop.pos_authorization:
authorization_id = sale.shop.pos_authorization.id
elif sale.invoice_type == 'M' and sale.shop.manual_authorization:
authorization_id = sale.shop.manual_authorization.id
2021-01-13 18:53:05 +01:00
elif sale.invoice_type == 'C' and sale.shop.computer_authorization:
authorization_id = sale.shop.computer_authorization.id
2021-01-13 14:16:24 +01:00
elif sale.invoice_type in ['1', '2', '3'] and sale.shop.electronic_authorization:
authorization_id = sale.shop.electronic_authorization.id
elif sale.shop.debit_note_electronic_authorization and sale.invoice_type == '92':
authorization_id = sale.shop.debit_note_electronic_authorization.id
else:
if sale.shop.credit_note_electronic_authorization and sale.invoice_type in ['91', 'N']:
authorization_id = sale.shop.credit_note_electronic_authorization.id
return authorization_id
2020-04-15 21:47:31 +02:00
@classmethod
def process_pos(cls, sale):
pool = Pool()
Sequence = pool.get('ir.sequence.strict')
Date = pool.get('ir.date')
# We need to keep that sequence for invoices been taked from shop
# configuration instead fiscalyear, because some countries sequences
# are setted by shop
number = None
sequence_id = None
if not sale:
return
invoice = sale.create_invoice()
sale.set_invoice_state()
sequence_id = sale.get_sequence(sale)
if not sale.invoice_number and sale.invoice_method == 'order':
if sequence_id:
number = Sequence.get_id(sequence_id)
sale_date = Date.today()
cls.write([sale], {
'invoice_number': number,
'invoice_date': sale_date,
})
else:
number = sale.invoice_number
position = sale.position if sale.position else None
if invoice:
if sale.invoice_date:
inv_date = sale.invoice_date
2021-04-06 14:46:38 +02:00
elif sale.shipment_date:
inv_date = sale.shipment_date
2020-04-15 21:47:31 +02:00
else:
inv_date = Date.today()
sale.invoice = invoice.id
2020-12-20 20:57:25 +01:00
to_write = {
2020-04-15 21:47:31 +02:00
'shop': sale.shop.id,
'invoice_date': inv_date,
'number': number,
'reference': sale.reference or sale.number,
'position': position,
2021-01-04 14:11:58 +01:00
'turn': sale.turn,
2020-12-20 20:57:25 +01:00
}
2020-04-15 21:47:31 +02:00
if sale.invoice_type:
2021-01-13 14:16:24 +01:00
pass
2020-12-20 20:57:25 +01:00
invoice.write([invoice], to_write)
2020-04-15 21:47:31 +02:00
sale.save()
return sale
@classmethod
def cancel(cls, sales):
pool = Pool()
Invoice = pool.get('account.invoice')
Journal = pool.get('account.journal')
super(Sale, cls).cancel(sales)
journals = Journal.search([
('type', '=', 'revenue'),
])
if journals:
journal = journals[0]
else:
return
for sale in sales:
if not sale.number or not sale.invoice_number:
continue
sale_invoices = list(sale.invoices)
if not sale.invoices:
invoice, = Invoice.create([{
'party': sale.party.id,
'account': sale.party.account_receivable.id,
'invoice_date': sale.invoice_date or sale.sale_date,
'payment_term': sale.payment_term.id,
'salesman': sale.salesman and sale.salesman.id,
'journal': journal.id,
'invoice_address': sale.invoice_address.id,
'number': sale.invoice_number,
'reference': sale.number
}])
sale_invoices.append(invoice)
Invoice.cancel(sale_invoices)
@staticmethod
def default_party():
User = Pool().get('res.user')
user = User(Transaction().user)
return user.shop.party.id if user.shop and user.shop.party else None
2021-02-09 17:17:01 +01:00
@fields.depends('invoice_method', 'pre_sale')
2020-04-15 21:47:31 +02:00
def on_change_pre_sale(self):
2021-01-04 14:11:58 +01:00
if self.pre_sale:
2020-04-15 21:47:31 +02:00
self.invoice_method = 'shipment'
else:
self.invoice_method = 'order'
@classmethod
def create(cls, vlist):
now = datetime.now()
vlist = [x.copy() for x in vlist]
for vals in vlist:
vals['pos_create_date'] = now
return super(Sale, cls).create(vlist)
@classmethod
def delete(cls, sales):
for sale in sales:
if sale.invoice_number:
2021-06-22 20:38:42 +02:00
raise SaleDeleteError(
gettext('sale_pos.msg_delete_invoice_number', sale=sale.rec_name, s=sale.invoice_number))
2020-04-15 21:47:31 +02:00
super(Sale, cls).delete(sales)
@classmethod
@ModelView.button_action('sale_pos.wizard_add_product')
def wizard_add_product(cls, sales):
pass
@classmethod
def process_sale_pos(cls, sale):
# We must set directly state and amounts cache because is faster
2021-02-20 16:25:06 +01:00
# 'state': 'confirmed',
2020-04-15 21:47:31 +02:00
cls.write([sale], {
2021-02-20 16:25:06 +01:00
'state': 'processing',
2020-04-15 21:47:31 +02:00
'untaxed_amount_cache': sale.untaxed_amount,
'tax_amount_cache': sale.tax_amount,
'total_amount_cache': sale.total_amount
})
sale = cls.process_pos(sale)
return sale
@classmethod
def update_state(cls, sales):
for sale in sales:
if not sale.is_done():
continue
@classmethod
def do_stock_moves(cls, sales):
for sale in sales:
2021-05-18 17:27:56 +02:00
if sale.shipment_state == "none":
sale.create_shipment('out')
sale.set_shipment_state()
2020-04-15 21:47:31 +02:00
@classmethod
def post_invoices(cls, sale):
pool = Pool()
Date = pool.get('ir.date')
Invoice = pool.get('account.invoice')
Sequence = pool.get('ir.sequence.strict')
invoice = None
if sale.invoice:
invoice = sale.invoice
else:
if sale.invoices:
invoice = sale.invoices[0]
if invoice:
2020-04-24 15:45:22 +02:00
if invoice.payment_term.id != sale.payment_term.id:
invoice.payment_term = sale.payment_term
2020-04-15 21:47:31 +02:00
_invoice_date = sale.invoice_date or Date.today()
if invoice.state == 'draft':
if not getattr(invoice, 'invoice_date', False):
invoice.invoice_date = _invoice_date
if not getattr(invoice, 'accounting_date', False):
invoice.accounting_date = _invoice_date or date.today()
2021-01-29 18:16:33 +01:00
if invoice.invoice_type not in ('P', 'M', 'C'):
2020-04-15 21:47:31 +02:00
Invoice.validate_invoice([invoice])
if hasattr(Invoice, 'submit'):
Invoice.submit([invoice])
if invoice.electronic_state == 'authorized':
Invoice.send_email([invoice])
2020-04-15 21:47:31 +02:00
2021-01-29 17:36:03 +01:00
if invoice.invoice_type in ('P', 'M', 'C') or (
2020-04-15 21:47:31 +02:00
hasattr(invoice, 'cufe') and invoice.cufe):
2021-04-13 18:51:42 +02:00
if hasattr(sale.shop, 'workflow_invoice') \
and sale.shop.workflow_invoice == 'validated':
2020-04-15 21:47:31 +02:00
Invoice.write([invoice], {'state': 'validated'})
else:
sale = cls.process_pos(sale)
if sale.invoice_number:
invoice.number = sale.invoice_number
else:
2021-03-31 21:33:38 +02:00
turn = sale._get_turn()
sale.turn = turn
invoice.turn = turn
2020-04-15 21:47:31 +02:00
sequence_id = sale.get_sequence(sale)
if sequence_id:
number = Sequence.get_id(sequence_id)
sale.invoice_number = number
sale.save()
invoice.number = number
invoice.save()
Invoice.post([invoice])
@classmethod
def do_reconcile(cls, sales):
Reconciliation = Pool().get('account.move.reconciliation')
for sale in sales:
reconcile_lines = []
account_reconcile_id = None
invoice_id = None
if not sale.payments or not sale.number:
continue
try:
for invoice in sale.invoices:
if invoice.state == 'paid' or not invoice.move:
continue
invoice_id = invoice.id
account_reconcile_id = invoice.account.id
for line in invoice.payment_lines:
if not line.reconciliation and line.account.id == account_reconcile_id:
reconcile_lines.append(line.id)
for l in invoice.move.lines:
if l.account.id == account_reconcile_id:
reconcile_lines.append(l.id)
break
for st_line in sale.payments:
st_line.invoice = invoice_id
st_line.save()
if not st_line.move:
st_line.create_move()
for ml in st_line.move.lines:
if not ml.reconciliation and ml.account.id == account_reconcile_id:
reconcile_lines.append(ml.id)
if reconcile_lines:
Reconciliation.create([{
'lines': [('add', reconcile_lines)],
'date': TODAY,
}])
2020-12-20 20:57:25 +01:00
sale.write([sale], {'state': 'done'})
2020-04-15 21:47:31 +02:00
except Exception:
print('Warning: Sale number not processed %s' % sale.number)
@classmethod
def workflow_to_end(cls, sales):
_sales = cls.process_sale_pos(sales[0])
cls.post_invoices(_sales)
cls.do_stock_moves([_sales])
cls.do_reconcile([_sales])
cls.update_state([_sales])
@classmethod
def get_paid_amount(cls, sales, names):
result = {n: {s.id: Decimal(0) for s in sales} for n in names}
for name in names:
for sale in sales:
for payment in sale.payments:
result[name][sale.id] += payment.amount
result[name][sale.id] += sale.get_total_vouchers_amount()
return result
2021-01-04 13:50:22 +01:00
def get_residual_amount(self, name=None):
2021-03-03 20:20:22 +01:00
total = 0
2021-03-03 17:54:55 +01:00
if self.total_amount_cache:
total = self.total_amount_cache
return (total - self.paid_amount)
2020-04-15 21:47:31 +02:00
@classmethod
@ModelView.button_action('sale_pos.wizard_sale_payment')
def wizard_sale_payment(cls, sales):
pass
@classmethod
@ModelView.button
def wizard_generate_invoice(cls, sales):
cls.quote(sales)
cls.workflow_to_end(sales)
@classmethod
@ModelView.button
def wizard_generate_shipment(cls, sales):
cls.do_stock_moves(sales)
@classmethod
def copy(cls, sales, default=None):
if default is None:
default = {}
default['payments'] = None
default['invoice'] = None
default['invoice_number'] = None
default['invoice_date'] = None
if sales:
sale = sales[0]
if sale.invoice and sale.invoice.number:
default['reference'] = sale.invoice.number
default['invoice_number'] = None
return super(Sale, cls).copy(sales, default)
def get_sequence(self, sale):
sequence_id = None
if sale.untaxed_amount_cache >= 0:
2020-04-15 21:47:31 +02:00
if sale.invoice_type == 'C' and sale.shop.computer_authorization:
sequence_id = sale.shop.computer_authorization.sequence.id
elif sale.invoice_type == 'P' and sale.shop.pos_authorization:
sequence_id = sale.shop.pos_authorization.sequence.id
elif sale.invoice_type == 'M' and sale.shop.manual_authorization:
sequence_id = sale.shop.manual_authorization.sequence.id
elif sale.invoice_type in ['1','2','3'] and sale.shop.electronic_authorization:
sequence_id = sale.shop.electronic_authorization.sequence.id
elif sale.shop.invoice_sequence:
sequence_id = sale.shop.invoice_sequence.id
else:
if sale.shop.credit_note_electronic_authorization and sale.invoice_type in ['91', 'N']:
sequence_id = sale.shop.credit_note_electronic_authorization.sequence.id
elif sale.shop.debit_note_electronic_authorization and sale.invoice_type == '92':
sequence_id = sale.shop.debit_note_electronic_authorization.sequence.id
else:
if sale.shop.credit_note_sequence:
sequence_id = sale.shop.credit_note_sequence.id
return sequence_id
def create_shipment(self, shipment_type):
if self.self_pick_up:
return self.create_moves_without_shipment(shipment_type)
return super(Sale, self).create_shipment(shipment_type)
def create_moves_without_shipment(self, shipment_type):
pool = Pool()
Move = pool.get('stock.move')
if self.shipment_method != 'order':
return
moves = {}
for line in self.lines:
move = line.get_move(shipment_type)
if move:
moves[line.id] = move
to_create = []
for m in moves:
2021-02-09 17:17:01 +01:00
moves[m].state = 'draft'
2020-04-15 21:47:31 +02:00
to_create.append(moves[m]._save_values)
Move.create(to_create)
Move.do(self.moves)
self.set_shipment_state()
2021-02-09 17:17:01 +01:00
# WTF
# @fields.depends('lines', 'currency', 'party')
# def on_change_lines(self):
# '''
# Overrides this method completely if the sale is self pick up to improve
# performance: Computes untaxed, total and tax amounts from the already
# computed values in sale lines.
# '''
#
# self.untaxed_amount = Decimal('0.0')
# self.tax_amount = Decimal('0.0')
# self.total_amount = Decimal('0.0')
#
# if self.lines:
# self.untaxed_amount = reduce(lambda x, y: x + y,
# [(getattr(l, 'amount', None) or Decimal(0))
# for l in self.lines if l.type == 'line'], Decimal(0)
# )
# self.total_amount = reduce(lambda x, y: x + y,
# [(getattr(l, 'amount_w_tax', None) or Decimal(0))
# for l in self.lines if l.type == 'line'], Decimal(0)
# )
# if self.currency:
# self.untaxed_amount = self.currency.round(self.untaxed_amount)
# self.total_amount = self.currency.round(self.total_amount)
# self.tax_amount = self.total_amount - self.untaxed_amount
# if self.currency:
# self.tax_amount = self.currency.round(self.tax_amount)
2020-04-15 21:47:31 +02:00
def get_invoice_number(self, name=None):
if self.invoices:
return self.invoices[0].number
@classmethod
def dash_on_change_party(cls, args, ctx={}):
if args.get('party'):
Party = Pool().get('party.party')
party = Party(args.get('party')['id'])
address = party.address_get(type='invoice')
return {
'invoice_address': {'id': address.id, 'name': address.name},
'shipment_address': None
}
2021-02-09 17:17:01 +01:00
def _get_turn(self):
Statement = Pool().get('account.statement')
if not self.sale_device:
return
statements = Statement.search([
('state', '=', 'draft'),
('sale_device', '=', self.sale_device.id),
('turn', '!=', None),
])
if statements:
return statements[0].turn
2020-04-15 21:47:31 +02:00
class SaleLine(metaclass=PoolMeta):
__name__ = 'sale.line'
2021-02-09 17:17:01 +01:00
# @fields.depends('unit_price', 'unit_price_full', 'product', 'quantity')
# def on_change_unit_price_full(self, name=None):
# pool = Pool()
# Tax = pool.get('account.tax')
# if self.product and self.unit_price_full and self.product.template.customer_taxes_used:
# res = Tax.reverse_compute(self.unit_price_full,
# self.product.template.customer_taxes_used)
# self.unit_price = res
2020-04-15 21:47:31 +02:00
@classmethod
def dash_on_change_line(cls, args, ctx):
if not args.get('quantity') or not args.get('product'):
return {}
Line = Pool().get('sale.line')
Sale = Pool().get('sale.sale')
Product = Pool().get('product.product')
PriceListLine = Pool().get('product.price_list.line')
product_id = args['product']['id']
product = Product(product_id)
company_id = ctx.get('company')
with Transaction().set_context(ctx):
unit_price = product.list_price
if args.get('quantity'):
quantity = int(args.get('quantity'))
else:
quantity = 1
if ctx.get('price_list'):
price_list_id = ctx.get('price_list')['id']
else:
price_list_id = None
percent_commission = 0
if price_list_id:
price_lines = PriceListLine.search([
('price_list', '=', price_list_id),
('product', '=', product_id),
])
if price_lines:
price_line = price_lines[0]
unit_price = float(unit_price)
unit_price = Decimal(eval(price_line.formula))
percent_commission = price_line.price_list.percent_commission
sale = Sale(company=company_id)
taxes_ids = [t.id for t in product.customer_taxes_used]
line = Line(
sale=sale.id,
type='line',
taxes=taxes_ids,
unit_price=unit_price,
quantity=quantity,
product=product.id,
)
res = cls.get_price_with_tax([line], ['amount_w_tax', 'unit_price_w_tax'])
res = {
'unit_price_w_tax': math.ceil(res['unit_price_w_tax'][None]),
'amount_w_tax': math.ceil(res['amount_w_tax'][None]),
'unit_price': math.ceil(unit_price),
'taxes': [[('add'), taxes_ids]],
'unit': product.template.default_uom.id,
'type': 'line',
}
if percent_commission:
res['commission_amount'] = round((((unit_price) * quantity) * percent_commission), 0)
return res
@classmethod
def __setup__(cls):
super(SaleLine, cls).__setup__()
# Allow edit product, quantity and unit in lines without parent sale
for fname in ('product', 'quantity', 'unit'):
field = getattr(cls, fname)
if field.states.get('readonly'):
del field.states['readonly']
@staticmethod
def default_sale():
if Transaction().context.get('sale'):
return Transaction().context.get('sale')
return None
2021-02-09 17:17:01 +01:00
# @fields.depends('sale')
# def on_change_product(self):
# Sale = Pool().get('sale.sale')
#
# if not self.sale:
# sale_id = Transaction().context.get('sale')
# if sale_id:
# self.sale = Sale(sale_id)
# super(SaleLine, self).on_change_product()
2020-04-15 21:47:31 +02:00
@fields.depends('sale')
def on_change_with_amount(self):
if not self.sale:
self.sale = Transaction().context.get('sale')
return super(SaleLine, self).on_change_with_amount()
def get_from_location(self, name):
res = super(SaleLine, self).get_from_location(name)
if self.sale.self_pick_up:
if self.warehouse and self.quantity >= 0:
return self.warehouse.storage_location.id
return res
def get_to_location(self, name):
res = super(SaleLine, self).get_to_location(name)
if self.sale.self_pick_up:
if self.warehouse and self.quantity < 0:
return self.warehouse.storage_location.id
return res
class StatementLine(metaclass=PoolMeta):
__name__ = 'account.statement.line'
sale = fields.Many2One('sale.sale', 'Sale', ondelete='RESTRICT')
class SaleReportSummary(metaclass=PoolMeta):
__name__ = 'sale_pos.sales_summary'
@classmethod
2021-06-22 20:38:42 +02:00
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
2020-04-15 21:47:31 +02:00
sum_untaxed_amount = Decimal(0)
sum_tax_amount = Decimal(0)
sum_total_amount = Decimal(0)
for sale in records:
sum_untaxed_amount += sale.untaxed_amount
sum_tax_amount += sale.tax_amount
sum_total_amount += sale.total_amount
report_context['sum_untaxed_amount'] = sum_untaxed_amount
report_context['sum_tax_amount'] = sum_tax_amount
report_context['sum_total_amount'] = sum_total_amount
report_context['company'] = report_context['user'].company
return report_context
class SaleReportSummaryByParty(metaclass=PoolMeta):
__name__ = 'sale_pos.sales_summary_by_party'
@classmethod
2021-06-22 20:38:42 +02:00
def get_context(cls, records, header, data):
2020-04-15 21:47:31 +02:00
parties = {}
2021-06-22 20:38:42 +02:00
report_context = super().get_context(records, header, data)
2020-04-15 21:47:31 +02:00
report_context['start_date'] = report_context['end_date'] = \
records[0].sale_date if records else None
sum_untaxed_amount = Decimal(0)
sum_tax_amount = Decimal(0)
sum_total_amount = Decimal(0)
for sale in records:
sum_untaxed_amount += sale.untaxed_amount
sum_tax_amount += sale.tax_amount
sum_total_amount += sale.total_amount
if sale.party.id not in parties.keys():
party = sale.party
party.name = sale.party.full_name
party.untaxed_amount = sale.untaxed_amount
party.tax_amount = sale.tax_amount
party.total_amount = sale.total_amount
party.currency = sale.currency
else:
party = parties.get(sale.party.id)
party.untaxed_amount += sale.untaxed_amount
party.tax_amount += sale.tax_amount
party.total_amount += sale.total_amount
parties[sale.party.id] = party
if sale.sale_date:
if not report_context['start_date'] or report_context['start_date'] > sale.sale_date:
report_context['start_date'] = sale.sale_date
if not report_context['end_date'] or report_context['end_date'] < sale.sale_date:
report_context['end_date'] = sale.sale_date
report_context['parties'] = parties.values()
report_context['sum_untaxed_amount'] = sum_untaxed_amount
report_context['sum_tax_amount'] = sum_tax_amount
report_context['sum_total_amount'] = sum_total_amount
report_context['company'] = report_context['user'].company
return report_context
2021-02-09 17:17:01 +01:00
# class AddProductForm(ModelView):
# 'Add Product Form'
# __name__ = 'sale_pos.add_product_form'
# sale = fields.Many2One('sale.sale', 'Sale')
# lines = fields.One2Many('sale.line', None, 'Lines',
# context={'sale': Eval('sale')}, depends=['sale'],)
#
#
# class WizardAddProduct(Wizard):
# 'Wizard Add Product'
# __name__ = 'sale_pos.add_product'
# start = StateView('sale_pos.add_product_form',
# 'sale_pos.add_product_view_form', [
# Button('Cancel', 'end', 'tryton-cancel'),
# Button('Add and New', 'add_new_', 'tryton-go-jump', default=True),
# Button('Add', 'add_', 'tryton-ok'),
# ])
# add_new_ = StateTransition()
# add_ = StateTransition()
#
# def default_start(self, fields):
# return {
# 'sale': Transaction().context.get('active_id'),
# }
#
# def add_lines(self):
# for line in self.start.lines:
# line.sale = Transaction().context.get('active_id', False)
# line.save()
#
# def transition_add_new_(self):
# self.add_lines()
# return 'start'
#
# def transition_add_(self):
# self.add_lines()
# return 'end'
2020-04-15 21:47:31 +02:00
class SaleForceDraft(Wizard):
'Sale Force Draft'
__name__ = 'sale_pos.force_draft'
start_state = 'force_draft'
force_draft = StateTransition()
@classmethod
def __setup__(cls):
super(SaleForceDraft, cls).__setup__()
def transition_force_draft(self):
sale_table = Table('sale_sale')
invoice_table = Table('account_invoice')
move_table = Table('account_move')
stock_move_table = Table('stock_move')
Sale = Pool().get('sale.sale')
Date = Pool().get('ir.date')
cursor = Transaction().connection.cursor()
ids = Transaction().context['active_ids']
if not ids:
return 'end'
sales = Sale.browse(ids)
for sale in sales:
# if sale.sale_date != Date.today():
# return 'end'
# The payments must be delete
for pay in sale.payments:
if pay.statement.state != 'draft':
return 'end'
if pay.move:
self.unreconcile_move(pay.move)
pay.move.delete([pay.move])
pay.delete([pay])
# The invoices must be delete
for invoice in sale.invoices:
if (hasattr(invoice, 'cufe') and invoice.cufe) or \
hasattr(invoice, 'electronic_state') and \
invoice.electronic_state == 'submitted':
2021-06-22 20:38:42 +02:00
raise SaleForceDraftError(
gettext('sale_pos.msg_with_electronic_invoice'))
2020-04-15 21:47:31 +02:00
if invoice.state == 'paid':
self.unreconcile_move(invoice.move)
if invoice.move:
cursor.execute(*move_table.update(
columns=[move_table.state],
values=['draft'],
where=move_table.id == invoice.move.id)
)
cursor.execute(*move_table.delete(
where=move_table.id == invoice.move.id)
)
cursor.execute(*invoice_table.update(
columns=[invoice_table.state, invoice_table.number],
values=['validate', None],
where=invoice_table.id == invoice.id)
)
cursor.execute(*invoice_table.delete(
where=invoice_table.id == invoice.id)
)
if sale.id:
cursor.execute(*sale_table.update(
2021-05-18 17:27:56 +02:00
columns=[sale_table.state, sale_table.shipment_state, sale_table.invoice_state],
values=['draft', 'none', 'none'],
2020-04-15 21:47:31 +02:00
where=sale_table.id == sale.id)
)
# The stock moves must be delete
stock_moves = [m.id for line in sale.lines for m in line.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))
)
return 'end'
def unreconcile_move(self, move):
Reconciliation = Pool().get('account.move.reconciliation')
reconciliations = [l.reconciliation for l in move.lines if l.reconciliation]
if reconciliations:
Reconciliation.delete(reconciliations)
class SaleUpdateDateStart(ModelView):
'Sale Update Date Start'
__name__ = 'sale_pos.sale_update_date.start'
new_date = fields.Date('New Date', required=True)
class SaleUpdateDate(Wizard):
'Sale Update Date'
__name__ = 'sale_pos.sale_update_date'
start = StateView('sale_pos.sale_update_date.start',
'sale_pos.sale_update_date_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'accept', 'tryton-ok', default=True),
])
accept = StateTransition()
def transition_accept(self):
sale = Table('sale_sale')
id_ = Transaction().context['active_id']
cursor = Transaction().connection.cursor()
if id_:
cursor.execute(*sale.update(
columns=[sale.sale_date],
values=[self.start.new_date],
where=sale.id == id_)
)
return 'end'
class SaleDetailedStart(ModelView):
'Sale Detailed Start'
__name__ = 'sale_pos.sale_detailed.start'
start_date = fields.Date('Start Date', required=True)
end_date = fields.Date('End Date', required=True)
company = fields.Many2One('company.company',
'Company', required=True)
salesman = fields.Many2One('company.employee', 'Salesman')
party = fields.Many2One('party.party', 'Party')
product = fields.Many2One('product.product', 'Product')
shop = fields.Many2One('sale.shop', 'Shop')
@staticmethod
def default_company():
return Transaction().context.get('company')
class SaleDetailed(Wizard):
'Sale Detailed'
__name__ = 'sale_pos.sale_detailed'
start = StateView('sale_pos.sale_detailed.start',
'sale_pos.sale_detailed_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
2021-02-17 19:38:45 +01:00
])
2020-04-15 21:47:31 +02:00
print_ = StateAction('sale_pos.report_sale_detailed')
@classmethod
def __setup__(cls):
super(SaleDetailed, cls).__setup__()
def do_print_(self, action):
salesman_id = None
party_id = None
product_id = None
shop_id = None
if self.start.salesman:
salesman_id = self.start.salesman.id
if self.start.shop:
shop_id = self.start.shop.id
if self.start.party:
party_id = self.start.party.id
if self.start.product:
product_id = self.start.product.id
data = {
'company': self.start.company.id,
'start_date': self.start.start_date,
'end_date': self.start.end_date,
'salesman': salesman_id,
'party': party_id,
'product': product_id,
'shop': shop_id,
}
return action, data
def transition_print_(self):
return 'end'
class SaleDetailedReport(Report):
__name__ = 'sale_pos.report_sale_detailed'
@classmethod
2021-06-22 20:38:42 +02:00
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
2020-04-15 21:47:31 +02:00
pool = Pool()
2021-02-17 19:38:45 +01:00
InvoiceLine = pool.get('account.invoice.line')
2020-04-15 21:47:31 +02:00
Company = pool.get('company.company')
sales_line_dom = [
2021-02-17 19:38:45 +01:00
('invoice.invoice_date', '>=', data['start_date']),
('invoice.invoice_date', '<=', data['end_date']),
('product', '!=', None),
('invoice.company', '=', data['company']),
('invoice.state', 'in', ['posted', 'paid', 'validated']),
2020-04-15 21:47:31 +02:00
]
if data['salesman']:
sales_line_dom.append(
2021-02-17 19:38:45 +01:00
('invoice.salesman', '=', data['salesman'])
2020-04-15 21:47:31 +02:00
)
if data['shop']:
sales_line_dom.append(
2021-02-17 19:38:45 +01:00
('invoice.shop', '=', data['shop'])
2020-04-15 21:47:31 +02:00
)
if data['party']:
sales_line_dom.append(
2021-02-17 19:38:45 +01:00
('invoice.party', '=', data['party'])
2020-04-15 21:47:31 +02:00
)
if data['product']:
sales_line_dom.append(
('product', '=', data['product'])
2021-02-17 19:38:45 +01:00
)
2020-04-15 21:47:31 +02:00
2021-02-17 19:38:45 +01:00
start_lines = InvoiceLine.search(sales_line_dom, order=[
('invoice.invoice_date', 'ASC')]
)
untaxed_amount = []
subtotal_amount = []
2020-04-15 21:47:31 +02:00
for line in start_lines:
2020-05-04 18:00:02 +02:00
if line.type == 'line' and not line.product:
2021-06-22 20:38:42 +02:00
raise SaleValidationError(
gettext('sale_shop.msg_sale_not_product', s=line.sale.number))
2021-02-17 19:38:45 +01:00
amount = line.quantity * float(line.product.sale_price_taxed)
line.subtotal_amount = amount
untaxed_amount.append(line.amount)
subtotal_amount.append(amount)
2020-04-15 21:47:31 +02:00
2021-02-17 19:38:45 +01:00
report_context['records'] = start_lines
report_context['untaxed_amount'] = sum(untaxed_amount)
report_context['subtotal_amount'] = sum(subtotal_amount)
2020-04-15 21:47:31 +02:00
report_context['company'] = Company(data['company'])
return report_context
class SalePaymentForm(ModelView):
'Sale Payment Form'
__name__ = 'sale.payment.form'
journal = fields.Many2One('account.statement.journal', 'Statement Journal',
domain=[
('id', 'in', Eval('journals', [])),
], depends=['journals'], required=True)
journals = fields.One2Many('account.statement.journal', None,
'Allowed Statement Journals')
payment_amount = fields.Numeric('Payment amount', required=True,
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits'])
currency_digits = fields.Integer('Currency Digits')
party = fields.Many2One('party.party', 'Party', readonly=True)
require_voucher = fields.Boolean('Require Voucher',
depends=['journal'])
voucher = fields.Char('Voucher Number', states={
'required': Eval('require_voucher', False),
'invisible': Not(Eval('require_voucher', False)),
}, depends=['require_voucher'])
self_pick_up = fields.Boolean('Self Pick Up', readonly=True)
do_invoice = fields.Boolean('Do Invoice')
@fields.depends('journal', 'voucher')
def on_change_with_require_voucher(self):
if self.journal:
return self.journal.require_voucher
else:
return False
@classmethod
def default_require_voucher(cls):
return False
@classmethod
def default_do_invoice(cls):
return True
class WizardSalePayment(Wizard):
'Wizard Sale Payment'
__name__ = 'sale.payment'
start = StateView('sale.payment.form',
'sale_pos.sale_payment_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Pay', 'pay_', 'tryton-ok', default=True),
])
pay_ = StateTransition()
@classmethod
def __setup__(cls):
super(WizardSalePayment, cls).__setup__()
def default_start(self, fields):
pool = Pool()
Sale = pool.get('sale.sale')
User = pool.get('res.user')
sale = Sale(Transaction().context['active_id'])
user = User(Transaction().user)
sale_device = sale.sale_device or user.sale_device or False
if user.id != 0 and not sale_device:
2021-06-22 20:38:42 +02:00
raise SaleDeviceError(gettext('sale_pos.msg_not_sale_device'))
2020-04-15 21:47:31 +02:00
return {
'journal': sale_device.journal.id
if sale_device.journal else None,
'journals': [j.id for j in sale_device.journals],
'payment_amount': sale.total_amount - sale.paid_amount
if sale.paid_amount else sale.total_amount,
'currency_digits': sale.currency_digits,
'party': sale.party.id,
}
def transition_pay_(self):
pool = Pool()
User = pool.get('res.user')
user = User(Transaction().user)
Sale = pool.get('sale.sale')
Statement = pool.get('account.statement')
StatementLine = pool.get('account.statement.line')
active_id = Transaction().context.get('active_id', False)
sale = Sale(active_id)
form = self.start
device_id = user.sale_device.id if user.sale_device else sale.sale_device.id
statements = Statement.search([
('journal', '=', form.journal),
('state', '=', 'draft'),
('sale_device', '=', device_id),
], order=[('date', 'DESC')])
if not statements:
2021-06-22 20:38:42 +02:00
raise DraftStatementError(
gettext('sale_pos.msg_not_draft_statement', s=form.journal.name))
2020-04-15 21:47:31 +02:00
if not sale.number:
Sale.set_number([sale])
2021-06-22 20:38:42 +02:00
if not sale.party.account_receivable:
raise PartyMissingAccount(
2021-07-20 23:24:07 +02:00
gettext('sale_pos.msg_party_without_account_receivable', s=sale.party.name))
2021-06-22 20:38:42 +02:00
account = sale.party.account_receivable.id
2020-04-15 21:47:31 +02:00
if form.payment_amount:
payment = StatementLine(
statement=statements[0].id,
date=date.today(),
amount=form.payment_amount,
party=sale.party.id,
account=account,
description=self.start.voucher,
sale=active_id,
# number=self.start.voucher,
# voucher=self.start.voucher,
)
payment.save()
if sale.total_amount != sale.paid_amount:
return 'start'
sale.save()
if self.start.do_invoice:
# for inv in sale.invoices:
# if inv.state == 'posted':
# inv.write([inv], {'state': 'draft'})
Sale.workflow_to_end([sale])
return 'end'
class SaleIncomeDailyStart(ModelView):
'Sale Income Daily Start'
__name__ = 'sale_pos.sale_income_daily.start'
company = fields.Many2One('company.company', 'Company', required=True)
date = fields.Date('Date', required=True)
2020-07-04 20:56:48 +02:00
shop = fields.Many2One('sale.shop', 'Shop')
user = fields.Many2One('res.user', 'User')
2020-04-15 21:47:31 +02:00
# journal = fields.Many2One('account.statement.journal', 'Journal')
@staticmethod
def default_company():
return Transaction().context.get('company')
2020-07-04 15:08:07 +02:00
@staticmethod
def default_shop():
return Transaction().context.get('shop')
@staticmethod
def default_user():
return Transaction().user
2020-04-15 21:47:31 +02:00
@staticmethod
def default_date():
Date = Pool().get('ir.date')
return Date.today()
class SaleIncomeDaily(Wizard):
'Sale Income Daily'
__name__ = 'sale_pos.sale_income_daily'
start = StateView('sale_pos.sale_income_daily.start',
'sale_pos.sale_income_daily_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('sale_pos.sale_income_daily_report')
def do_print_(self, action):
shop_id, user_id = None, None
if self.start.user:
user_id = self.start.user.id
2020-04-15 21:47:31 +02:00
if self.start.shop:
shop_id = self.start.shop.id
report_context = {
'company': self.start.company.id,
'date': self.start.date,
'user': user_id,
2020-04-15 21:47:31 +02:00
'shop': shop_id,
}
return action, report_context
def transition_print_(self):
return 'end'
class SaleIncomeDailyReport(Report):
'Income Daily Report'
__name__ = 'sale_pos.sale_income_daily_report'
@classmethod
2021-06-22 20:38:42 +02:00
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
2020-04-15 21:47:31 +02:00
pool = Pool()
Invoice = pool.get('account.invoice')
Voucher = pool.get('account.voucher')
Company = pool.get('company.company')
company = Company(data['company'])
Statement = pool.get('account.statement')
Shop = pool.get('sale.shop')
User = pool.get('res.user')
2020-07-15 14:09:28 +02:00
records = []
2021-02-16 05:21:40 +01:00
devices = []
statements_ = {}
2020-07-15 14:09:28 +02:00
advances = []
total_advances = []
advances_cash = []
advances_electronic = []
total_statements = []
statement_cash = []
statement_electronic = []
advances_voucher = []
total_payments = []
payments = []
payments_voucher = []
payments_cash = []
payments_electronic = []
2020-04-15 21:47:31 +02:00
2020-07-04 20:56:48 +02:00
dom_statement = [
2020-04-15 21:47:31 +02:00
('date', '=', data['date']),
2020-07-04 20:56:48 +02:00
]
if data['shop']:
dom_statement.append(('sale_device.shop.id', '=', data['shop']))
statements = Statement.search(dom_statement)
2020-04-15 21:47:31 +02:00
user_id = Transaction().user
user = User(user_id)
for statement in statements:
st_amount = sum(l.amount for l in statement.lines)
2021-02-16 05:35:42 +01:00
to_add = {
'journal': statement.journal.name,
'turn': statement.turn,
'total_amount': st_amount,
}
2021-02-16 05:21:40 +01:00
if statement.sale_device.id not in statements_.keys():
2021-02-16 05:35:42 +01:00
statements_[statement.sale_device.id] = {
2021-02-16 05:21:40 +01:00
'name': statement.sale_device.name,
'total': [st_amount],
2021-02-16 05:35:42 +01:00
'records': [to_add]
}
2021-02-16 05:21:40 +01:00
else:
statements_[statement.sale_device.id]['total'].append(st_amount)
2021-02-16 05:35:42 +01:00
statements_[statement.sale_device.id]['records'].append(to_add)
2020-04-15 21:47:31 +02:00
total_statements.append(st_amount)
for l in statement.lines:
2020-07-10 00:13:04 +02:00
if l.statement.journal.kind == 'cash':
statement_cash.append(l.amount)
else:
statement_electronic.append(l.amount)
2020-04-15 21:47:31 +02:00
dom_vouchers = [
2020-07-04 15:08:07 +02:00
('move', '!=', None),
('move.post_date', '=', data['date']),
2020-04-15 21:47:31 +02:00
]
if data['user']:
dom_vouchers.append(
('create_uid', '=', data['user']),
)
2020-04-15 21:47:31 +02:00
vouchers = Voucher.search(dom_vouchers)
for v in vouchers:
cash = 0
electronic = 0
for l in v.lines:
2020-07-15 14:09:28 +02:00
if v.voucher_type == 'receipt':
if v.payment_mode.payment_type == 'cash':
advances_cash.append(l.amount)
cash = l.amount
else:
advances_electronic.append(l.amount)
electronic = l.amount
advances_voucher.append(l.amount)
advances.append({
'number': l.voucher.number,
'reference': l.detail,
'party': l.party.name if l.party else l.voucher.party.name,
'total_amount': l.amount,
'payment_mode': l.voucher.payment_mode.name,
'cash': cash,
'electronic': electronic,
})
total_advances.append(l.amount)
if v.voucher_type == 'payment':
amount_ = l.amount * (-1)
if v.payment_mode.payment_type == 'cash':
payments_cash.append(amount_)
cash = amount_
else:
payments_electronic.append(amount_)
electronic = amount_
payments_voucher.append(amount_)
payments.append({
'number': l.voucher.number,
'reference': l.detail,
'party': l.party.name if l.party else l.voucher.party.name,
'total_amount': amount_,
'payment_mode': l.voucher.payment_mode.name,
'cash': cash,
'electronic': electronic,
})
total_payments.append(amount_)
2020-04-15 21:47:31 +02:00
2020-07-04 20:56:48 +02:00
dom_invoices = [
2020-04-15 21:47:31 +02:00
('company', '=', data['company']),
('invoice_date', '=', data['date']),
('number', '!=', None),
2021-02-16 05:21:40 +01:00
('state', 'in', ['posted', 'paid', 'canceled', 'validated']),
2021-03-03 17:03:35 +01:00
('type', '=', 'out'),
2020-04-15 21:47:31 +02:00
]
shop_names = ''
if data['shop']:
shop_names = Shop(data['shop']).name
2020-07-04 20:56:48 +02:00
dom_invoices.append(
2020-04-15 21:47:31 +02:00
('shop', '=', data['shop'])
)
else:
shops = Shop.search([])
for s in shops:
shop_names += s.name + ', '
2020-07-04 20:56:48 +02:00
invoices = Invoice.search(dom_invoices, order=[('number', 'ASC')])
2020-04-15 21:47:31 +02:00
invoices_number = []
total_invoices_cash = []
total_invoices_electronic = []
total_invoices_credit = []
total_invoices_paid = []
2021-02-16 05:21:40 +01:00
devices = {}
2020-04-15 21:47:31 +02:00
total_invoices_amount = []
for invoice in invoices:
invoices_number.append(invoice.number)
cash = 0
electronic = 0
credit = 0
total_invoices_amount.append(invoice.total_amount)
sale = invoice.sales[0]
2021-02-16 05:21:40 +01:00
device_id = sale.sale_device.id
2020-04-15 21:47:31 +02:00
if not sale.payments:
total_invoices_credit.append(invoice.amount_to_pay)
credit = invoice.amount_to_pay
else:
for p in sale.payments:
if p.statement.date == data['date']:
if p.statement.journal.kind == 'cash':
cash += p.amount
total_invoices_cash.append(p.amount)
else:
electronic += p.amount
total_invoices_electronic.append(p.amount)
total_invoices_paid.append(p.amount)
2021-02-16 05:21:40 +01:00
paid = cash + electronic
2020-04-15 21:47:31 +02:00
inv = {
'number': invoice.number,
'reference': invoice.reference,
'party': invoice.party.name,
'total_amount': invoice.total_amount,
'credit': credit,
'cash': cash,
'electronic': electronic,
2021-02-16 05:21:40 +01:00
'paid': paid,
2020-04-15 21:47:31 +02:00
'state': invoice.state_string,
2021-02-13 21:39:09 +01:00
'sale_device': sale.sale_device.name,
2020-04-15 21:47:31 +02:00
}
records.append(inv)
2021-02-16 05:21:40 +01:00
try:
devices[device_id]['total'].append(invoice.total_amount)
devices[device_id]['cash'].append(cash)
devices[device_id]['electronic'].append(electronic)
2021-02-16 05:35:42 +01:00
devices[device_id]['credit'].append(credit)
2021-02-16 05:21:40 +01:00
devices[device_id]['paid'].append(paid)
except:
devices[device_id] = {
'name': sale.sale_device.name,
'total': [invoice.total_amount],
'credit': [credit],
'cash': [cash],
'electronic': [electronic],
'paid': [paid],
}
2020-04-15 21:47:31 +02:00
advances_cash_ = sum(advances_cash)
advances_electronic_ = sum(advances_electronic)
statement_cash_ = sum(statement_cash)
statement_electronic_ = sum(statement_electronic)
2020-07-15 14:09:28 +02:00
payments_cash_ = sum(payments_cash)
payments_electronic_ = sum(payments_electronic)
2020-04-15 21:47:31 +02:00
report_context['records'] = records
2021-02-16 05:21:40 +01:00
report_context['devices'] = devices.values()
2020-04-15 21:47:31 +02:00
report_context['advances'] = advances
2021-02-16 05:35:42 +01:00
report_context['statements'] = statements_.values()
2020-04-15 21:47:31 +02:00
report_context['date'] = data['date']
report_context['company'] = company.party.name
report_context['shop'] = shop_names
report_context['user'] = user.name
report_context['print_date'] = datetime.now()
report_context['statement_cash'] = statement_cash_
report_context['statement_electronic'] = statement_electronic_
report_context['total_invoices_amount'] = sum(total_invoices_amount)
report_context['total_invoices_cash'] = sum(total_invoices_cash)
report_context['total_invoices_electronic'] = sum(total_invoices_electronic)
report_context['total_invoices_credit'] = sum(total_invoices_credit)
report_context['total_invoices_paid'] = sum(total_invoices_paid)
report_context['total_advances'] = sum(total_advances)
report_context['advances_cash'] = advances_cash_
report_context['advances_electronic'] = advances_electronic_
report_context['advances_voucher'] = sum(advances_voucher)
report_context['total_statements'] = sum(total_statements)
report_context['start_invoice'] = min(invoices_number) if invoices_number else ''
report_context['end_invoice'] = max(invoices_number) if invoices_number else ''
report_context['total_cash'] = advances_cash_ + statement_cash_
report_context['total_electronic'] = advances_electronic_ + statement_electronic_
2020-07-15 14:09:28 +02:00
report_context['total_payments'] = sum(total_payments)
report_context['payments_voucher'] = sum(payments_voucher)
report_context['payments_cash'] = payments_cash_
report_context['payments_electronic'] = payments_electronic_
report_context['payments'] = payments
2020-04-15 21:47:31 +02:00
return report_context
2020-04-22 01:18:59 +02:00
2020-12-28 03:32:05 +01:00
class SaleByKindStart(ModelView):
'Sale by Kind Start'
__name__ = 'sale_pos.sale_by_kind.start'
company = fields.Many2One('company.company', 'Company', required=True)
start_date = fields.Date('Start Date', required=True)
end_date = fields.Date('End Date', required=True)
shop = fields.Many2One('sale.shop', 'Shop')
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_shop():
return Transaction().context.get('shop')
@staticmethod
def default_date():
Date = Pool().get('ir.date')
return Date.today()
class SaleByKind(Wizard):
'Sale By Kind'
__name__ = 'sale_pos.sale_by_kind'
start = StateView('sale_pos.sale_by_kind.start',
'sale_pos.sale_by_kind_start_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('sale_pos.sale_by_kind_report')
def do_print_(self, action):
shop_id = None
if self.start.shop:
shop_id = self.start.shop.id
report_context = {
'company': self.start.company.id,
'start_date': self.start.start_date,
'end_date': self.start.end_date,
'shop': shop_id,
}
return action, report_context
def transition_print_(self):
return 'end'
class SaleByKindReport(Report):
'Sale by Kind Report'
__name__ = 'sale_pos.sale_by_kind_report'
@classmethod
2021-06-22 20:38:42 +02:00
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
2020-12-28 03:32:05 +01:00
pool = Pool()
Sale = pool.get('sale.sale')
Company = pool.get('company.company')
company = Company(data['company'])
dom = [
('company', '=', company),
('sale_date', '>=', data['start_date']),
('sale_date', '<=', data['end_date']),
2020-12-28 04:21:31 +01:00
('state', 'in', ('done', 'processing')),
2020-12-28 03:32:05 +01:00
]
if data['shop']:
dom.append(('shop', '=', data['shop']))
records = {}
sales = Sale.search(dom)
for sale in sales:
if sale.kind:
kind = sale.kind
else:
2020-12-28 04:21:31 +01:00
kind = 'others'
2020-12-28 03:32:05 +01:00
if sale.shop:
shop = sale.shop.name
else:
2020-12-28 04:21:31 +01:00
shop = 'others'
2020-12-28 03:32:05 +01:00
shop_id = str(shop)
amount_cache = sale.total_amount_cache
if shop_id not in records:
records[shop_id] = {
'shop': shop,
2020-12-28 04:21:31 +01:00
'kind_others': [],
2020-12-28 03:32:05 +01:00
'kind_to_table': [],
'kind_delivery': [],
'kind_catering': [],
2020-12-28 03:32:05 +01:00
'kind_take_away': [],
2020-12-30 00:14:00 +01:00
'sale_amount': []
2020-12-28 03:32:05 +01:00
}
2020-12-28 04:21:31 +01:00
if kind == 'others':
records[shop_id]['kind_others'].append(amount_cache)
elif kind == 'to_table':
records[shop_id]['kind_to_table'].append(amount_cache)
elif kind == 'delivery':
records[shop_id]['kind_delivery'].append(amount_cache)
elif kind == 'take_away':
records[shop_id]['kind_take_away'].append(amount_cache)
elif kind == 'catering':
records[shop_id]['kind_catering'].append(amount_cache)
2020-12-28 04:21:31 +01:00
records[shop_id]["sale_amount"].append(amount_cache)
total_amount = 0
total_amount_delivery = 0
total_amount_others = 0
total_amount_take_away = 0
total_amount_to_table = 0
total_amount_catering = 0
2020-12-28 04:21:31 +01:00
total_sales = 0
total_sales_delivery = 0
total_sales_others = 0
total_sales_take_away = 0
total_sales_to_table = 0
total_sales_catering = 0
2020-12-28 03:32:05 +01:00
for r in records.values():
2020-12-28 04:21:31 +01:00
total_amount_others += sum(r['kind_others'])
total_sales_others += len(r['kind_others'])
2020-12-28 03:32:05 +01:00
total_amount_to_table += sum(r['kind_to_table'])
total_sales_to_table += len(r['kind_to_table'])
total_amount_delivery += sum(r['kind_delivery'])
total_sales_delivery += len(r['kind_delivery'])
total_amount_take_away += sum(r['kind_take_away'])
total_sales_take_away += len(r['kind_take_away'])
total_amount_catering += sum(r['kind_catering'])
total_sales_catering += len(r['kind_catering'])
2020-12-28 03:32:05 +01:00
total_amount += sum(r['sale_amount'])
total_sales += len(r['sale_amount'])
report_context['records'] = records.values()
report_context['company'] = company.rec_name
2020-12-28 04:21:31 +01:00
report_context['total_amount_others'] = total_amount_others
report_context['total_sales_others'] = total_sales_others
2020-12-28 03:32:05 +01:00
report_context['total_amount_delivery'] = total_amount_delivery
report_context['total_sales_delivery'] = total_sales_delivery
report_context['total_amount_take_away'] = total_amount_take_away
report_context['total_sales_take_away'] = total_sales_take_away
report_context['total_amount_to_table'] = total_amount_to_table
report_context['total_sales_to_table'] = total_sales_to_table
report_context['total_amount_catering'] = total_amount_catering
report_context['total_sales_catering'] = total_sales_catering
2020-12-28 03:32:05 +01:00
report_context['total_amount'] = total_amount
report_context['total_sales'] = total_sales
report_context['data'] = data
return report_context
2020-04-22 01:18:59 +02:00
class DeleteSalesDraft(Wizard):
'Delete Sales Draft'
__name__ = 'sale_pos.delete_sales_draft'
start_state = 'delete_sales_draft'
delete_sales_draft = StateTransition()
@classmethod
def __setup__(cls):
super(DeleteSalesDraft, cls).__setup__()
def transition_delete_sales_draft(self):
2020-08-10 03:33:11 +02:00
cursor = Transaction().connection.cursor()
2020-04-22 01:18:59 +02:00
Sale = Pool().get('sale.sale')
sales = Sale.search(['AND',
2020-12-26 15:12:46 +01:00
['OR', [
('state', '=', 'draft'),
('invoice_number', '=', ''),
], [
('state', '=', 'draft'),
('invoice_number', '=', None),
],
]])
2020-04-22 01:18:59 +02:00
sales_delete = []
for sale in sales:
2020-08-10 03:33:11 +02:00
# if sale.lines:
# continue
2020-04-22 01:18:59 +02:00
if sale.payments:
continue
2020-08-10 03:33:11 +02:00
sales_delete.append(str(sale.id))
# Sale.delete(sales_delete)
cursor.execute("DELETE from sale_sale WHERE \
2020-12-26 15:12:46 +01:00
id in (%s)" % (', '.join(sales_delete)))
2020-04-22 01:18:59 +02:00
return 'end'
2020-08-27 21:18:37 +02:00
class PortfolioPaymentsStart(ModelView):
'Portfolio Payments Start'
__name__ = 'sale_pos.portfolio_payments.start'
company = fields.Many2One('company.company', 'Company', required=True)
start_date = fields.Date('Start Date', required=True)
end_date = fields.Date('End Date', required=True)
user = fields.Many2One('res.user', 'User')
salesman = fields.Many2One('company.employee', 'Salesman')
2020-08-27 21:18:37 +02:00
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_start_date():
today_ = date.today()
month = today_.month
day = today_.day
year = today_.year
if month == 1:
old_month = 12
else:
old_month = month - 1
return date(year, old_month, day)
@staticmethod
def default_end_date():
return date.today()
class PortfolioPayments(Wizard):
'Portfolio Payments'
__name__ = 'sale_pos.portfolio_payments'
start = StateView('sale_pos.portfolio_payments.start',
'sale_pos.portfolio_payments_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('sale_pos.portfolio_payments_report')
def do_print_(self, action):
data = {
'start_date': self.start.start_date,
'end_date': self.start.end_date,
'company': self.start.company.id,
'company_name': self.start.company.rec_name,
}
if self.start.user:
data['user'] = self.start.user.id
if self.start.salesman:
data['salesman'] = self.start.salesman.id
2020-08-27 21:18:37 +02:00
return action, data
def transition_print_(self):
return 'end'
class PortfolioPaymentsReport(Report):
'Portfolio Payments Report'
__name__ = 'sale_pos.portfolio_payments_report'
@classmethod
2021-06-22 20:38:42 +02:00
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
2020-08-27 21:18:37 +02:00
pool = Pool()
VoucherLine = pool.get('account.voucher.line')
today = date.today()
dom_search = [
('voucher.date', '>=', data['start_date']),
('voucher.date', '<=', data['end_date']),
('voucher.company', '=', data['company']),
('voucher.state', 'not in', ['cancel', 'draft']),
]
if data.get('user'):
dom_search.append(
('create_uid', '=', data['user']),
)
voucher_lines = []
for line in VoucherLine.search(dom_search):
voucher = line.voucher
move_line = line.move_line
if not move_line:
continue
origin = move_line.origin
expiration_date = move_line.maturity_date
2020-08-27 22:56:02 +02:00
expiration_days = (today - expiration_date).days \
if expiration_date else None
2020-08-27 21:18:37 +02:00
if origin and origin.__name__ == 'account.invoice':
2020-09-01 16:03:16 +02:00
shop = origin.shop.rec_name if origin.shop else ''
salesman = origin.salesman.rec_name if origin.salesman else ''
salesman_id = origin.salesman.id if origin.salesman else None
2020-08-27 21:18:37 +02:00
number = origin.number
invoice_date = origin.invoice_date
invoice_amount = origin.total_amount
invoice_balance = origin.amount_to_pay
else:
shop = ''
salesman = ''
number = move_line.reference
invoice_date = ''
invoice_amount = move_line.amount
invoice_balance = None
values = {
2020-08-27 21:18:37 +02:00
'number': voucher.number,
'shop': shop,
'salesman': salesman,
'salesman_id': salesman_id,
2020-08-27 21:18:37 +02:00
'party_id': voucher.party.id_number_full,
'party_name': voucher.party.name,
'date': voucher.date,
'payment_mode': voucher.payment_mode.rec_name,
'check_number': voucher.check_number,
'payment_amount': line.amount,
'invoice_number': number,
'invoice_date': invoice_date,
'expiration_date': expiration_date,
'invoice_amount': invoice_amount,
'invoice_balance': invoice_balance,
'expiration_days': expiration_days,
2021-07-20 23:24:07 +02:00
'state': voucher.state_string,
}
2021-07-20 23:24:07 +02:00
if voucher.state == 'posted':
values['post_date'] = voucher.move.post_date
if data.get('salesman'):
if data['salesman'] == values['salesman_id']:
voucher_lines.append(values)
else:
voucher_lines.append(values)
2020-08-27 21:18:37 +02:00
report_context['records'] = voucher_lines
report_context['company'] = data['company_name']
report_context['sum_amount'] = sum([obj['payment_amount'] for obj in voucher_lines])
return report_context
2021-05-18 17:27:56 +02:00
class SaleGenerateShipment(Wizard):
"""
Sale Generate Shipment
"""
__name__ = 'sale.sale.generate_shipment'
start_state = 'generate_shipment'
generate_shipment = StateTransition()
@classmethod
def __setup__(cls):
super(SaleGenerateShipment, cls).__setup__()
def transition_generate_shipment(self):
Sale = Pool().get('sale.sale')
ids = Transaction().context['active_ids']
if ids:
2021-05-18 17:36:11 +02:00
sales = Sale.browse(ids)
for sale in sales:
2021-05-20 15:38:52 +02:00
if sale.shipment_state != 'none':
2021-06-22 20:38:42 +02:00
continue
2021-05-20 15:38:52 +02:00
if sale.total_amount_cache < 0:
2021-05-18 17:36:11 +02:00
sale.create_shipment('return')
sale.set_shipment_state()
2021-05-20 15:38:52 +02:00
else:
2021-05-18 17:36:11 +02:00
sale.create_shipment('out')
sale.set_shipment_state()
2021-05-18 17:27:56 +02:00
return 'end'