From 13f67ecac225a4ddbecc4ca1604dc35aa361d7cb Mon Sep 17 00:00:00 2001 From: Oscar Date: Thu, 3 Feb 2022 22:40:32 -0500 Subject: [PATCH] Remove test file --- sale-new.py | 2052 --------------------------------------------------- sale.py | 13 + 2 files changed, 13 insertions(+), 2052 deletions(-) delete mode 100644 sale-new.py diff --git a/sale-new.py b/sale-new.py deleted file mode 100644 index b8487b7..0000000 --- a/sale-new.py +++ /dev/null @@ -1,2052 +0,0 @@ -# This file is part of Tryton. The COPYRIGHT file at the top level of -# this repository contains the full copyright notices and license terms. -import math -from decimal import Decimal -from datetime import datetime, date, timedelta -from itertools import chain - -from sql import Table -from sql import Null -from sql.operators import NotIn -from sql.aggregate import Sum, Count -from sql.conditionals import Case - -from trytond.model import ModelView, fields -from trytond.pool import PoolMeta, Pool -from trytond.transaction import Transaction -from trytond.pyson import Bool, Eval, Not -from trytond.wizard import ( - Wizard, StateView, StateAction, StateReport, StateTransition, Button -) -from trytond.report import Report -from trytond.i18n import gettext -from .exceptions import ( - ProductMissingTaxError, ImportSalesError, SaleDeleteError, - SaleForceDraftError, SaleDeviceError, DraftStatementError, - PartyMissingAccount -) -from trytond.modules.sale.exceptions import SaleValidationError - -_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) - invoice_number = fields.Char('Invoice Number') - invoice_date = fields.Date('Invoice Date') - invoice = fields.Many2One('account.invoice', 'Invoice') - # Migrated to sale_shop - # payments = fields.One2Many('account.statement.line', 'sale', 'Payments') - # paid_amount = fields.Function(fields.Numeric('Paid Amount', digits=(16, 2)), - # 'get_paid_amount') - # residual_amount = fields.Function(fields.Numeric('Residual Amount', - # digits=(16, 2), readonly=True), 'get_residual_amount') - sale_device = fields.Many2One('sale.device', 'Sale Device', - domain=[('shop', '=', Eval('shop'))], depends=['shop'], states=STATES) - 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) - turn = fields.Integer('Turn', states=STATES) - - @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') - 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) - - cls._buttons.update({ - '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: - if sale.invoices: - invoice = sale.invoices[0] - if invoice.state in ('posted', 'paid'): - continue - # cls.recheck_taxes(sale) - cls.process_pos(sale) - super(Sale, cls).process(sales) - - # @classmethod - # def recheck_taxes(cls, sale): - # for line in sale.lines: - # vat_required = None - # if line.product and line.product.account_category: - # for txr in line.product.account_category.customer_taxes_used: - # if txr.type == 'percentage' and txr.rate > 0: - # vat_required = txr.id - # break - # 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: - # raise ProductMissingTaxError( - # gettext('sale_pos.msg_missing_tax', s=line.product.rec_name)) - - 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: - if not l.product or (not hasattr(l.product, 'extra_tax') or not l.product.extra_tax): - continue - raw_val = Decimal(float(l.product.extra_tax) * l.quantity) - val = self.currency.round(raw_val) - amounts.append(val) - return res + sum(amounts) - - # @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)) - # raise ImportSalesError( - # gettext('sale_pos.msg_import_data_sale', field='producto', s=not_product)) - # - # 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)) - # raise ImportSalesError( - # gettext('sale_pos.msg_import_data_sale', field='tercero', s=not_party)) - # - # 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: - # not_party - # raise ImportSalesError( - # gettext('sale_pos.msg_import_data_sale', field='modo de pago', s=row[10])) - # 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 - - 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 - - sale = sales[0] - invoice = self._get_invoice_sale() - if self.invoice_type: - invoice.authorization = self._get_authorization(sale) - invoice.invoice_type = self.invoice_type - if getattr(invoice, 'lines', None): - invoice_lines = list(invoice.lines) + invoice_lines - invoice.lines = invoice_lines - invoice.turn = self._get_turn() - invoice.save() - Invoice.update_taxes([invoice]) - return invoice - - def _get_authorization(self, sale): - authorization_id = None - if sale.untaxed_amount_cache >= 0: - 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 - elif sale.invoice_type == 'C' and sale.shop.computer_authorization: - authorization_id = sale.shop.computer_authorization.id - 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 - - @classmethod - def process_pos(cls, sale): - if not sale: - return - - if sale.invoices: - invoice = sale.invoices[0] - if invoice.number and invoice.state != 'draft': - return sale - pool = Pool() - 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 - invoice = sale.create_invoice() - sale.set_invoice_state() - sequence = sale.get_sequence(sale) - if not sale.invoice_number and sale.invoice_method == 'order': - if sequence: - number = sequence.get() - 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 - elif sale.shipment_date: - inv_date = sale.shipment_date - else: - inv_date = Date.today() - - sale.invoice = invoice.id - to_write = { - 'shop': sale.shop.id, - 'invoice_date': inv_date, - 'number': number, - 'reference': sale.reference or sale.number, - 'position': position, - 'turn': sale.turn, - } - - if sale.invoice_type: - pass - invoice.write([invoice], to_write) - - 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 - - @fields.depends('invoice_method', 'pre_sale') - def on_change_pre_sale(self): - if self.pre_sale: - 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: - raise SaleDeleteError( - gettext('sale_pos.msg_delete_invoice_number', sale=sale.rec_name, s=sale.invoice_number)) - 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 - # 'state': 'confirmed', - cls.write([sale], { - 'state': 'processing', - '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: - if sale.shipment_state == "none": - sale.create_shipment('out') - sale.set_shipment_state() - - @classmethod - def post_invoices(cls, sale): - pool = Pool() - Date = pool.get('ir.date') - Invoice = pool.get('account.invoice') - invoice = None - if sale.invoice: - invoice = sale.invoice - else: - if sale.invoices: - invoice = sale.invoices[0] - - if invoice: - if invoice.payment_term.id != sale.payment_term.id: - invoice.payment_term = sale.payment_term - - _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() - if invoice.invoice_type not in ('P', 'M', 'C'): - Invoice.validate_invoice([invoice]) - if hasattr(Invoice, 'submit'): - Invoice.submit([invoice]) - if invoice.electronic_state == 'authorized': - Invoice.send_email([invoice]) - - if invoice.invoice_type in ('P', 'M', 'C') or ( - hasattr(invoice, 'cufe') and invoice.cufe): - if hasattr(sale.shop, 'workflow_invoice') \ - and sale.shop.workflow_invoice == 'validated': - Invoice.write([invoice], {'state': 'validated'}) - else: - sale_ = cls.process_pos(sale) - if sale_: - sale = sale_ - if sale.invoice_number: - invoice.number = sale.invoice_number - else: - turn = sale._get_turn() - sale.turn = turn - invoice.turn = turn - sequence = sale.get_sequence(sale) - - if sequence: - number = sequence.get() - 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, - }]) - sale.write([sale], {'state': 'done'}) - except Exception: - print('Warning: Sale number not processed %s' % sale.number) - - @classmethod - def workflow_to_end(cls, sales): - sale = sales[0] - _sale = cls.process_sale_pos(sale) - if _sale: - sale = _sale - - cls.post_invoices(sale) - cls.do_stock_moves([sale]) - cls.do_reconcile([sale]) - cls.update_state([sale]) - - @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): - print('sequence ------ function') - sequence = None - if sale.untaxed_amount_cache >= 0: - if sale.invoice_type == 'C' and sale.shop.computer_authorization: - sequence = sale.shop.computer_authorization.sequence - elif sale.invoice_type == 'P' and sale.shop.pos_authorization: - sequence = sale.shop.pos_authorization.sequence - elif sale.invoice_type == 'M' and sale.shop.manual_authorization: - sequence = sale.shop.manual_authorization.sequence - elif sale.invoice_type in ['1','2','3'] and sale.shop.electronic_authorization: - sequence = sale.shop.electronic_authorization.sequence - elif sale.shop.invoice_sequence: - sequence = sale.shop.invoice_sequence - else: - if sale.shop.credit_note_electronic_authorization and sale.invoice_type in ['91', 'N']: - sequence = sale.shop.credit_note_electronic_authorization.sequence - elif sale.shop.debit_note_electronic_authorization and sale.invoice_type == '92': - sequence = sale.shop.debit_note_electronic_authorization.sequence - else: - if sale.shop.credit_note_sequence: - sequence = sale.shop.credit_note_sequence - return sequence - - 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: - moves[m].state = 'draft' - to_create.append(moves[m]._save_values) - - Move.create(to_create) - Move.do(self.moves) - self.set_shipment_state() - - # 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) - - 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 - } - - 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 - - -class SaleLine(metaclass=PoolMeta): - __name__ = 'sale.line' - - # @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 - - @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 - - # @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() - - @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 - - def compute_price_w_tax(self, line): - Tax = Pool().get('account.tax') - res = line.unit_price * Decimal(line.quantity) - - if line.taxes and line.unit_price: - tax_list = Tax.compute(line.taxes, - line.unit_price or Decimal('0.0'), 1) - res = sum([t['amount'] for t in tax_list], Decimal('0.0')) - res = (res + line.unit_price) * Decimal(line.quantity) - if line.product.extra_tax: - res += line.product.extra_tax * Decimal(line.quantity) - res = res.quantize( - Decimal(1) / 10 ** self.__class__.unit_price.digits[1]) - return res - - -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') - MoveLine = Pool().get('account.move.line') - 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.invoice = None - pay.save() - 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': - raise SaleForceDraftError( - gettext('sale_pos.msg_with_electronic_invoice')) - if invoice.move: - move = invoice.move - MoveLine.check_journal_period_modify(move.period, move.journal) - 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( - columns=[sale_table.state, sale_table.shipment_state, sale_table.invoice_state], - values=['draft', 'none', 'none'], - 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), - ]) - 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 - def get_context(cls, records, header, data): - from operator import itemgetter - report_context = super().get_context(records, header, data) - pool = Pool() - Invoice = pool.get('account.invoice') - InvoiceLine = pool.get('account.invoice.line') - Company = pool.get('company.company') - Product = pool.get('product.product') - - invoice_dom = [ - ('invoice_date', '>=', data['start_date']), - ('invoice_date', '<=', data['end_date']), - ('company', '=', data['company']), - ('type', '=', 'out'), - ('state', 'in', ['posted', 'paid', 'validated']), - ] - - if data['salesman']: - invoice_dom.append( - ('salesman', '=', data['salesman']) - ) - if data['shop']: - invoice_dom.append( - ('shop', '=', data['shop']) - ) - if data['party']: - invoice_dom.append( - ('party', '=', data['party']) - ) - - fields_inv = [ - 'id', 'number', 'invoice_date', 'state', 'payment_term.name', - 'shop.name', 'party.name' - ] - - invoices = Invoice.search_read(invoice_dom, fields_names=fields_inv, - order=[('invoice_date', 'ASC')] - ) - line_dom = [ - ('product', '!=', None), - ('type', '=', 'line'), - ] - if data['product']: - line_dom.append(('product', '=', data['product'])) - - invoices_ids = {inv['id']: inv for inv in invoices} - line_dom.append(('invoice', 'in', invoices_ids.keys())) - fields_lines = [ - 'id', 'product.name', 'unit.symbol', 'quantity', 'invoice', - 'product.last_cost', 'product.cost_price', 'unit_price', - 'product.account_category.name', 'taxes' - ] - - lines = InvoiceLine.search_read(line_dom, fields_names=fields_lines) - untaxed_amount = [] - total_amount = [] - untaxed_amount_append = untaxed_amount.append - total_amount_append = total_amount.append - get_prices_taxed = Product.get_prices_taxed - dgetter = itemgetter( - 'invoice', 'unit_price', 'quantity', 'product.', 'taxes' - ) - pt_getter = itemgetter( - 'id', 'name', 'cost_price', 'last_cost', 'account_category.' - ) - for line in lines: - invoice, unit_price, qty, product, taxes = dgetter(line) - product_id, name, cost_price, last_cost, category = pt_getter(product) - line.update(invoices_ids[invoice]) - cost_price = cost_price or last_cost or 0 - amount = round(qty * float(unit_price), 2) - utility = unit_price - cost_price - - cost_taxed, sale_price_taxed = get_prices_taxed( - product_id, cost_price, unit_price, taxes - ) - amount_taxed = float(sale_price_taxed) * qty - - line.update({ - 'amount': amount, - 'cost_price': cost_price, - 'product': name, - 'amount_taxed': amount_taxed, - 'total_cost': float(cost_taxed) * qty, - 'utility': float(utility) * qty, - 'utility_rate': (utility / cost_price) if cost_price else 0, - 'sale_price_taxed': sale_price_taxed, - 'cost_price_taxed': cost_taxed, - 'category': category['name'], - 'shop': line['shop.']['name'] if line.get('shop.') else '', - }) - untaxed_amount_append(amount) - total_amount_append(amount_taxed) - report_context['records'] = lines - report_context['untaxed_amount'] = sum(untaxed_amount) - report_context['total_amount'] = sum(total_amount) - 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: -# raise SaleDeviceError(gettext('sale_pos.msg_not_sale_device')) -# return { -# 'journal': sale_device.journal.id -# if sale_device.journal else None, -# 'journals': [j.id for j in sale_device.journals], -# 'payment_amount': abs(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 -# if form.payment_amount > 0: -# device_id = user.sale_device.id if user.sale_device else sale.sale_device.id -# statements = Statement.search([ -# ('journal', '=', form.journal.id), -# ('state', '=', 'draft'), -# ('sale_device', '=', device_id), -# ], order=[('date', 'DESC')]) -# if not statements: -# raise DraftStatementError( -# gettext('sale_pos.msg_not_draft_statement', journal=form.journal.name)) -# -# if not sale.number: -# Sale.set_number([sale]) -# -# if not sale.party.account_receivable: -# raise PartyMissingAccount( -# gettext('sale_pos.msg_party_without_account_receivable', s=sale.party.name)) -# account = sale.party.account_receivable.id -# -# if form.payment_amount: -# amount = form.payment_amount -# if sale.total_amount < 0: -# amount = amount * -1 -# payment = StatementLine( -# statement=statements[0].id, -# date=date.today(), -# amount=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) - shop = fields.Many2One('sale.shop', 'Shop') - user = fields.Many2One('res.user', 'User') - # journal = fields.Many2One('account.statement.journal', 'Journal') - - @staticmethod - def default_company(): - return Transaction().context.get('company') - - @staticmethod - def default_shop(): - return Transaction().context.get('shop') - - @staticmethod - def default_user(): - return Transaction().user - - @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 - if self.start.shop: - shop_id = self.start.shop.id - report_context = { - 'company': self.start.company.id, - 'date': self.start.date, - 'user': user_id, - '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 - def get_context(cls, records, header, data): - report_context = super().get_context(records, header, data) - 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') - records = [] - devices = [] - statements_ = {} - advances = [] - total_advances = [] - advances_cash = [] - advances_electronic = [] - total_statements = [] - statement_cash = [] - statement_electronic = [] - advances_voucher = [] - total_payments = [] - payments = [] - payments_voucher = [] - payments_cash = [] - payments_electronic = [] - - dom_statement = [ - ('date', '=', data['date']), - ] - if data['shop']: - dom_statement.append(('sale_device.shop.id', '=', data['shop'])) - statements = Statement.search(dom_statement) - user_id = Transaction().user - user = User(user_id) - - for statement in statements: - st_amount = sum(l.amount for l in statement.lines) - to_add = { - 'journal': statement.journal.name, - 'turn': statement.turn, - 'total_amount': st_amount, - } - if statement.sale_device.id not in statements_.keys(): - statements_[statement.sale_device.id] = { - 'name': statement.sale_device.name, - 'total': [st_amount], - 'records': [to_add] - } - else: - statements_[statement.sale_device.id]['total'].append(st_amount) - statements_[statement.sale_device.id]['records'].append(to_add) - total_statements.append(st_amount) - - for l in statement.lines: - if l.statement.journal.kind == 'cash': - statement_cash.append(l.amount) - else: - statement_electronic.append(l.amount) - - dom_vouchers = [ - ('move', '!=', None), - ('move.post_date', '=', data['date']), - ] - if data['user']: - dom_vouchers.append( - ('create_uid', '=', data['user']), - ) - vouchers = Voucher.search(dom_vouchers) - for v in vouchers: - cash = 0 - electronic = 0 - for l in v.lines: - 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_) - - dom_invoices = [ - ('company', '=', data['company']), - ('invoice_date', '=', data['date']), - ('number', '!=', None), - # ('state', 'in', ['posted', 'paid', 'canceled', 'validated']), - ('type', '=', 'out'), - ] - shop_names = '' - if data['shop']: - shop_names = Shop(data['shop']).name - dom_invoices.append( - ('shop', '=', data['shop']) - ) - else: - shops = Shop.search([]) - for s in shops: - shop_names += s.name + ', ' - - invoices = Invoice.search(dom_invoices, order=[('number', 'ASC')]) - invoices_number = [] - total_invoices_cash = [] - total_invoices_electronic = [] - total_invoices_credit = [] - total_invoices_paid = [] - devices = {} - 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) - if not invoice.sales: - continue - sale = invoice.sales[0] - device_id = sale.sale_device.id - 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) - paid = cash + electronic - inv = { - 'number': invoice.number, - 'reference': invoice.reference, - 'party': invoice.party.name, - 'total_amount': invoice.total_amount, - 'credit': credit, - 'cash': cash, - 'electronic': electronic, - 'paid': paid, - 'state': invoice.state_string, - 'sale_device': sale.sale_device.name, - } - records.append(inv) - try: - devices[device_id]['total'].append(invoice.total_amount) - devices[device_id]['cash'].append(cash) - devices[device_id]['electronic'].append(electronic) - devices[device_id]['credit'].append(credit) - 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], - } - - advances_cash_ = sum(advances_cash) - advances_electronic_ = sum(advances_electronic) - statement_cash_ = sum(statement_cash) - statement_electronic_ = sum(statement_electronic) - payments_cash_ = sum(payments_cash) - payments_electronic_ = sum(payments_electronic) - - report_context['records'] = records - report_context['devices'] = devices.values() - report_context['advances'] = advances - report_context['statements'] = statements_.values() - 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_ - 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 - return report_context - - -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) - start_time = fields.Time('Start Time') - end_time = fields.Time('End Time') - 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, - 'start_time': self.start.start_time, - 'end_time': self.start.end_time, - '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 - def get_context(cls, records, header, data): - report_context = super().get_context(records, header, data) - pool = Pool() - Line = pool.get('sale.line') - Shop = pool.get('sale.shop') - Sale = pool.get('sale.sale') - Company = pool.get('company.company') - company = Company(data['company']) - cursor = Transaction().connection.cursor() - config = pool.get('sale.configuration')(1) - products_exception = [] - if config.tip_product and config.exclude_tip_and_delivery: - products_exception.append(config.tip_product.id) - if config.delivery_product and config.exclude_tip_and_delivery: - products_exception.append(config.delivery_product.id) - - line = Line.__table__() - shop = Shop.__table__() - sale = Sale.__table__() - - where = sale.state.in_(['done', 'processing']) - where &= sale.sale_date >= data['start_date'] - where &= sale.sale_date <= data['end_date'] - where &= sale.company >= data['company'] - - if data['start_time'] and data['end_time']: - start_date = datetime.combine(data['start_date'], data['start_time']) - timedelta(hours=5) - end_date = datetime.combine(data['end_date'], data['end_time']) - timedelta(hours=5) - - where &= sale.create_date <= end_date - where &= sale.create_date >= start_date - if len(products_exception) > 0: - where &= NotIn(line.product, products_exception) - if data['shop']: - where &= sale.shop == data['shop'] - - columns = [ - sale.shop.as_('shop_id'), - shop.name.as_('shop'), - Count(sale.number).as_('sales_total'), - Sum(line.quantity*line.unit_price).as_('amount_total'), - ] - - kinds = ['to_table', 'take_away', 'delivery', 'catering', 'others'] - for k in kinds: - if k != 'others': - columns_ = [ - Sum(Case((sale.kind == k, 1), else_=0)).as_('sales_'+k), - Sum(Case((sale.kind == k, (line.quantity*line.unit_price)), else_=0)).as_('amount_'+k), - ] - else: - columns_ = [ - Sum(Case((sale.kind == Null or sale.kind == '', 1), else_=0)).as_('sales_'+k), - Sum(Case((sale.kind == Null or sale.kind == '', (line.quantity*line.unit_price)), else_=0)).as_('amount_'+k), - ] - columns.extend(columns_) - - cursor.execute(*line.join( - sale, condition=line.sale == sale.id - ).join(shop, condition=sale.shop == shop.id).select( - *columns, - where=where, - group_by=(sale.shop, shop.name))) - columns = list(cursor.description) - result = cursor.fetchall() - records = [] - totals = {} - for row in result: - row_dict = {} - for i, col in enumerate(columns): - if col.name.startswith('sales') or col.name.startswith('amount'): - try: - totals[col.name] += row[i] - except: - totals[col.name] = row[i] - row_dict[col.name] = row[i] - records.append(row_dict) - report_context['records'] = records - report_context['company'] = company.rec_name - report_context['total'] = totals if len(totals) > 1 else None - report_context['data'] = data - return report_context - - -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): - cursor = Transaction().connection.cursor() - Sale = Pool().get('sale.sale') - sales = Sale.search(['AND', - ['OR', [ - ('state', '=', 'draft'), - ('invoice_number', '=', ''), - ], [ - ('state', '=', 'draft'), - ('invoice_number', '=', None), - ], - ]]) - sales_delete = [] - for sale in sales: - # if sale.lines: - # continue - if sale.payments: - continue - sales_delete.append(str(sale.id)) - # Sale.delete(sales_delete) - cursor.execute("DELETE from sale_sale WHERE \ - id in (%s)" % (', '.join(sales_delete))) - return 'end' - - -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') - is_post_date = fields.Boolean('Is Post Date') - - @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, - 'is_post_date': self.start.is_post_date, - } - if self.start.user: - data['user'] = self.start.user.id - - if self.start.salesman: - data['salesman'] = self.start.salesman.id - return action, data - - def transition_print_(self): - return 'end' - - -class PortfolioPaymentsReport(Report): - 'Portfolio Payments Report' - __name__ = 'sale_pos.portfolio_payments_report' - - @classmethod - def get_context(cls, records, header, data): - report_context = super().get_context(records, header, data) - pool = Pool() - VoucherLine = pool.get('account.voucher.line') - today = date.today() - dom_search = [ - - ('voucher.company', '=', data['company']), - ('voucher.state', '=', 'posted'), - ('voucher.voucher_type', '=', 'receipt'), - ] - if not data['is_post_date']: - dom_search.append([ - ('voucher.date', '>=', data['start_date']), - ('voucher.date', '<=', data['end_date']), ] - ) - else: - dom_search.append([ - ('voucher.move.post_date', '>=', data['start_date']), - ('voucher.move.post_date', '<=', data['end_date']), ] - ) - 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.move_origin - expiration_date = move_line.maturity_date - expiration_days = (today - expiration_date).days \ - if expiration_date else None - if origin and origin.__name__ == 'account.invoice': - 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 - number = origin.number - invoice_date = origin.invoice_date - invoice_amount = origin.total_amount - invoice_balance = origin.amount_to_pay - else: - shop = '' - salesman = '' - salesman_id = '' - number = move_line.reference - invoice_date = '' - invoice_amount = move_line.amount - invoice_balance = None - - values = { - 'number': voucher.number, - 'shop': shop, - 'salesman': salesman, - 'salesman_id': salesman_id, - '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, - 'state': voucher.state_string, - } - 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) - - 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 - - -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: - sales = Sale.browse(ids) - for sale in sales: - if sale.shipment_state != 'none': - continue - if sale.total_amount_cache < 0: - sale.create_shipment('return') - sale.set_shipment_state() - else: - sale.create_shipment('out') - sale.set_shipment_state() - return 'end' - - -class SalesCostsStart(ModelView): - 'Sales Costs Start' - __name__ = 'sale_pos.sales_costs.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') - - -class SalesCosts(Wizard): - 'Sales Costs' - __name__ = 'sale_pos.sales_costs' - start = StateView('sale_pos.sales_costs.start', - 'sale_pos.sales_costs_start_view_form', [ - Button('Cancel', 'end', 'tryton-cancel'), - Button('Print', 'print_', 'tryton-print', default=True), - ]) - print_ = StateReport('sale_pos.sales_costs_report') - - def do_print_(self, action): - shop_id = None - if self.start.shop: - shop_id = self.start.shop.id - data = { - 'company': self.start.company.id, - 'start_date': self.start.start_date, - 'end_date': self.start.end_date, - 'shop': shop_id, - } - return action, data - - def transition_print_(self): - return 'end' - - -class SalesCostsReport(Report): - __name__ = 'sale_pos.sales_costs_report' - - @classmethod - def get_line(cls, line, date_, number, origin): - return { - 'date': date_, - 'move': number, - 'party': line.party.name if line.party else '', - 'reference': line.reference, - 'description': line.description, - 'origin': origin, - 'debit': line.debit, - 'credit': line.credit, - } - - @classmethod - def get_context(cls, records, header, data): - report_context = super().get_context(records, header, data) - pool = Pool() - # Invoice = pool.get('account.invoice') - InvoiceLine = pool.get('account.invoice.line') - Company = pool.get('company.company') - Shop = pool.get('sale.shop') - company = Company(data['company']) - shop_name = '' - if data['shop']: - shop = Shop(data['shop']) - shop_name = shop.name - - dom = [ - ('invoice.invoice_date', '>=', data['start_date']), - ('invoice.invoice_date', '<=', data['end_date']), - ('invoice.move', '!=', None), - ('invoice_type', '=', 'out'), - ('type', '=', 'line'), - ] - if data['shop']: - dom.append(('invoice.shop', '=', data['shop'])) - - lines = InvoiceLine.search_read(dom, fields_names=[ - 'product', 'product.rec_name', 'product.code', 'quantity', - 'unit_price', 'unit.symbol', 'product.cost_price' - ]) - - records = {} - total_cost = [] - total_income = [] - append_cost = total_cost.append - append_income = total_income.append - for line in lines: - qty, unit_price = line['quantity'], line['unit_price'] - cost_price = line['product.']['cost_price'] - income_amount = qty * float(unit_price) - cost_amount = qty * float(cost_price) - append_cost(cost_amount) - append_income(income_amount) - try: - rec_product = records[line['product']] - rec_product['qty'].append(qty) - rec_product['income_amount'].append(income_amount) - rec_product['cost_amount'].append(cost_amount) - except: - code, name = line['product.']['code'], line['product.']['rec_name'] - records[line['product']] = { - 'code': code, - 'name': name, - 'category': '', - 'uom': line['unit.']['symbol'], - 'qty': [qty], - 'cost_price': cost_price, - 'income_amount': [income_amount], - 'cost_amount': [cost_amount], - } - - report_context['shop'] = shop_name - report_context['company'] = company - report_context['records'] = records.values() - report_context['total_cost'] = total_cost - report_context['total_income'] = total_income - return report_context - - -class WizardSalePayment(metaclass=PoolMeta): - __name__ = 'sale.payment' - - def transition_pay_(self): - res = super(WizardSalePayment, self).transition_pay_() - Sale = Pool().get('sale.sale') - active_id = Transaction().context.get('active_id', False) - sale = Sale(active_id) - if self.start.do_invoice: - Sale.workflow_to_end([sale]) - return res diff --git a/sale.py b/sale.py index 75a8e8c..78d244c 100644 --- a/sale.py +++ b/sale.py @@ -2078,3 +2078,16 @@ class SalesCostsReport(Report): report_context['total_cost'] = total_cost report_context['total_income'] = total_income return report_context + + +class WizardSalePayment(metaclass=PoolMeta): + __name__ = 'sale.payment' + + def transition_pay_(self): + res = super(WizardSalePayment, self).transition_pay_() + Sale = Pool().get('sale.sale') + active_id = Transaction().context.get('active_id', False) + sale = Sale(active_id) + if self.start.do_invoice: + Sale.workflow_to_end([sale]) + return res