# 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 operator import attrgetter from decimal import Decimal from sql import Table from sql.aggregate import Sum from datetime import date, timedelta from time import sleep from trytond.pool import Pool, PoolMeta from trytond.transaction import Transaction from trytond.model import fields from trytond.modules.dash.dash import DashAppBase from .process_pay import get_pay, get_response_pay, process_response, get_dict_response_pay class Sale(metaclass=PoolMeta): __name__ = 'sale.sale' @classmethod def dash_faster_process(cls, records): for rec in records: cls.faster_process({'id': rec.id}) @classmethod def dash_quote(cls, args, ctx=None): # Deprecation warning use create sale instead print("Deprecation Warning: use method create_sale instead") cls.create_sale(cls, args, ctx=None) @classmethod def create_sale(cls, args, ctx=None): Shop = Pool().get('sale.shop') Product = Pool().get('product.product') Party = Pool().get('party.party') User = Pool().get('res.user') ctx = Transaction().context print(ctx, 'ctx') if ctx.get('shop'): shop = Shop(ctx['shop']) if args.get('shop'): shop = Shop(args['shop']) else: user_id = ctx.get('user') user = User(user_id) shop = user.shop for v in args['lines']: if v.get('id'): del v['id'] if v.get('amount'): del v['amount'] if v.get('unit_price_w_tax'): del v['unit_price_w_tax'] if v.get('total_amount'): del v['total_amount'] v['type'] = 'line' v['unit_price'] = round(Decimal(v['unit_price']), 4) product = Product(v['product']) if v.get('discount') and v['discount'] != '': v['discount'] = Decimal(v['discount']) / 100 elif v.get('discount'): del v['discount'] v['unit'] = product.template.default_uom.id v['description'] = product.name taxes = list(product.account_category.customer_taxes_used) taxes_ids = [t.id for t in taxes] v['taxes'] = [('add', taxes_ids)] try: price_list = args['price_list']['id'] except: price_list = args.get('price_list', None) try: # option kid party, = Party.browse([args['party']['id']]) except: # option fastkid party, = Party.browse([args['party']]) if args.get('shipment_address'): try: # option kid shipment_address_id = args.get('shipment_address')['id'] except: # option fastkid shipment_address_id = args.get('shipment_address') else: shipment_address_id = party.addresses[0].id agent_id = None if args.get('agent'): try: # option kid agent_id = args['agent']['id'] except: # option fastkid agent_id = args['agent'] shipment_date = None if args.get('shipment_date'): shipment_date = args['shipment_date'] description = args.get('description', '') comment = args.get('comment', '') today = date.today() to_create = { 'shop': shop.id, 'invoice_type': 'P', 'company': shop.company.id, 'party': party.id, 'sale_date': today, 'shipment_date': shipment_date, 'shipment_address': shipment_address_id, 'invoice_address': shipment_address_id, 'agent': agent_id, 'price_list': price_list, 'payment_term': shop.payment_term.id, 'state': 'draft', 'description': description, 'comment': comment, 'lines': [('create', args['lines'])], } if args.get('consumer'): to_create['consumer'] = args['consumer'] sale, = cls.create([to_create]) # for line in sale.lines: # if line.discount and line.discount > 0: # line.on_change_discount() cls.write([sale], {'state': 'quotation'}) cls.set_number([sale]) sale.save() record = args.copy() record.update({ 'id': sale.id, 'state': sale.state, 'number': sale.number, 'total_amount': sale.total_amount, }) res = { 'record': record, 'msg': f'Pedido No. {sale.number}', 'type': 'success', } return res @classmethod def get_pay_from_card(cls, args): sale, = cls.browse([args['id']]) fields = ['total_amount', 'number', 'sale_taxes', 'shop'] total_amount, number, sale_taxes, shop = attrgetter(*fields)(sale) terminal = shop.datafono dic_values = { 'Operacion': '0', 'Valor': int(total_amount), 'Propina': 0, 'IVA': 0, 'Factura': number, 'Base Devolución IVA': 0, 'Código del Cajero': 0, 'Impuesto al Consumo': 0, 'Monto Base IVA': 0, 'Monto Base Impuesto al Consumo': 0, 'Recibo': number } for t in sale_taxes: if t == '01': dic_values['IVA'] = int(t['value_tax']) dic_values['Monto Base IVA'] = int(t['base']) elif t == '04': dic_values['Impuesto al Consumo'] = int(t['value_tax']) dic_values['Monto Base Impuesto al Consumo'] = int(t['base']) data = ','.join(map(str, dic_values.values())) # data = "0,15630,1200,2500,1234567,10630,987653,800,14000,1630,12345" # terminal = "AAC08581" response = get_pay(data, terminal) response_pay = process_response(response) print(response_pay, 'this is response pay') if response_pay == 'OK': res = { 'status': 'ok', 'terminal': terminal } else: res = { 'status': 'error' } return res @classmethod def get_response_pay_from_card(cls, args): # response_process_pay = None terminal = args['terminal'] response = get_response_pay(terminal) # for t in range(1, 17): # if t == 1: # sleep(20) # else: # sleep(10) # response = get_response_pay(terminal) # if response_process_pay is not None: # break response_process_pay = process_response(response) if response_process_pay: data_pay = get_dict_response_pay(response_process_pay) result = { 'pay': data_pay, 'status': 'ok', 'msg': 'Pago exitoso' } else: result = { 'pay': None, 'status': 'error', 'msg': 'error al procesar pago' } return result @classmethod def process_pay_sale(cls, args): pool = Pool() Device = pool.get('sale.device') StatementLine = pool.get('account.statement.line') Date = pool.get('ir.date') payment = args['pay'] sale_id = args['sale_id'] sale, = cls.browse([sale_id]) if sale.residual_amount == sale.total_amount: device, = Device.search([ ('shop', '=', sale.shop.id), ]) sale.sale_device = device payment_means_code = '48' if payment['tipo_cuenta'] == 'CR' else '49' journal_id = None journal_name = None for journal in device.journals: if journal.payment_means_code == payment_means_code: journal_id = journal.id journal_name = journal.rec_name break statement_open_id = cls.is_statement_open( journal_id, sale.sale_device.id) if not statement_open_id: return { 'status': 'error', 'message': f'Pago no se puede procesar; No se encontró un estado de cuenta abierto para el diario {journal_name}' } to_create = { 'sale': sale_id, 'date': Date.today(), 'statement': statement_open_id, 'amount': sale.total_amount, 'party': sale.party.id, 'account': sale.party.account_receivable.id, 'description': sale.invoice_number or '', 'number': payment['consecutivo_transaccion'] } line, = StatementLine.create([to_create]) line.create_move() cls.wizard_generate_invoice([sale]) barcode = sale.number data_order = cls.get_order2print({'sale_id': sale_id, 'repeat': True}) data_invoice = cls.get_data({'sale_id': sale_id, 'type_doc': 'invoice'}) data_invoice['barcode'] = barcode for d in data_order[0].values(): d['barcode'] = barcode ctx = sale.get_printing_context({'device_id': sale.sale_device.id}) # shop = sale.shop # ctx = { # 'company': sale.company.party.name, # 'sale_device': sale.sale_device.name, # 'shop': shop.name, # 'street': shop.address.street, # 'user': "app.user", # 'city': shop.address.city_code.name, # 'zip': "00000", # 'phone': sale.company.party.phone, # 'id_number': sale.company.party.id_number, # 'tax_regime': "NA", # } return { 'status': 'ok', 'data_order': data_order[0], 'data_invoice': data_invoice, 'ctx': ctx, } @classmethod def dash_get_line(cls, args, ctx): if not args.get('product'): return {} Product = Pool().get('product.product') PriceListLine = Pool().get('product.price_list.line') product_id = args['product']['id'] product = Product(product_id) context = { 'company': ctx['company'], 'currency': ctx['currency'], } price_list_id = None price_list = args.get('price_list', None) if price_list: price_list_id = price_list['id'] context['price_list'] = price_list_id with Transaction().set_context(context): unit_price = product.list_price 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 # ADD TAXES # taxes_ids = [t.id for t in product.customer_taxes_used] # res = cls.get_price_with_tax([line], ['amount_w_tax', 'unit_price_w_tax']) res = { 'unit_price_w_tax': math.ceil(product.sale_price_taxed), # 'amount_w_tax': math.ceil(res['amount_w_tax'][None]), # 'taxes': [[('add'), taxes_ids]], 'unit_price': math.ceil(unit_price), '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 dash_create_order_call(cls, args, ctx=None): Shop = Pool().get('sale.shop') Product = Pool().get('product.product') Party = Pool().get('party.party') User = Pool().get('res.user') ctx = Transaction().context if ctx.get('shop'): shop = Shop(ctx['shop']) else: user_id = ctx.get('user') user = User(user_id) shop = user.shop for v in args['lines']: if v.get('id'): del v['id'] if v.get('amount'): del v['amount'] if v.get('unit_price_w_tax'): del v['unit_price_w_tax'] if v.get('total_amount'): del v['total_amount'] v['type'] = 'line' product = Product(v['product']) if v.get('discount') and v['discount'] != '': v['discount'] = Decimal(v['discount'])/100 elif v.get('discount'): del v['discount'] if v.get('unit_price'): v['unit_price'] = Decimal(str(v['unit_price'])) v['unit'] = product.template.default_uom.id v['description'] = product.name taxes = list(product.account_category.customer_taxes_used) taxes_ids = [t.id for t in taxes] v['taxes'] = [('add', taxes_ids)] price_list = args.get('price_list', None) party = Party(args['party']) if args.get('shipment_address'): shipment_address_id = args.get('shipment_address') else: shipment_address_id = party.addresses[0].id agent_id = None if args.get('agent'): agent_id = args['agent'] shipment_date = None if args.get('shipment_date'): shipment_date = args['shipment_date'] description = args.get('description', '') comment = args.get('comment', '') today = date.today() to_create = { 'consumer': args['consumer'], 'source': args['source'], 'kind': args['kind'], 'shop': shop.id, 'salesman': user.employee, 'invoice_type': 'P', 'company': shop.company.id, 'party': party.id, 'sale_date': today, 'shipment_date': shipment_date, 'shipment_address': shipment_address_id, 'invoice_address': shipment_address_id, 'agent': agent_id, 'price_list': price_list, 'payment_term': shop.payment_term.id, 'state': 'requested', 'description': description, 'comment': comment, 'lines': [('create', args['lines'])], } if args.get('consumer'): to_create['consumer'] = args['consumer'] sale, = cls.create([to_create]) for line in sale.lines: if line.discount and line.discount > 0: line.on_change_discount() record = args.copy() record.update({ 'id': sale.id, 'state': sale.state, 'number': sale.number, 'total_amount': sale.total_amount, }) res = { 'record': record, 'msg': 'successful_order', 'type': 'success', 'open_modal': True, } return res @classmethod def mark_commanded(cls, args): """ This method mark as commanded all products in sale, previous positive response of local printer """ sale = cls(args['sale_id']) for line in sale.lines: line.order_sended = True line.save() @classmethod def _set_line(cls, val, context): del val['id'] Product = Pool().get('product.product') val['type'] = 'line' product = Product(val['product']) with Transaction().set_context(context): unit_price = product.list_price unit_price = unit_price.quantize(Decimal(str(10.0 ** -4))) val['unit_price'] = unit_price val['base_price'] = unit_price val['unit'] = product.template.default_uom.id val['description'] = product.name taxes = list(product.account_category.customer_taxes_used) taxes_ids = [t.id for t in taxes] val['taxes'] = [('add', taxes_ids)] return val @classmethod def command(cls, args): Shop = Pool().get('sale.shop') ShopTable = Pool().get('sale.shop.table') User = Pool().get('res.user') context = Transaction().context user_id = context.get('user') user = User(user_id) action = 'create' if args['id'] > 0: action = 'edit' table = None if args.get('table_assigned'): try: table_id = args.get('table_assigned')['id'] except: table_id = args.get('table_assigned') table = ShopTable(table_id) if action == 'create': if context.get('shop'): shop = Shop(context['shop']) else: shop = user.shop context['price_list'] = shop.price_list for v in args['lines']: cls._set_line(v, context) party = shop.party today = date.today() to_create = { 'shop': shop.id, 'party': party.id, 'invoice_type': 'P', 'table_assigned': table.id, 'shipment_address': party.addresses[0].id, 'invoice_address': party.addresses[0].id, 'company': shop.company.id, 'sale_date': today, 'shipment_date': today, 'kind': 'to_table', 'price_list': shop.price_list, 'payment_term': shop.payment_term.id, 'state': 'draft', 'salesman': user.employee.id if user.employee else None, 'order_status': 'commanded', 'sale_device': user.sale_device.id, 'lines': [('create', args['lines'])], } if args.get('description'): to_create['description'] = args.get('description') try: sale, = cls.create([to_create]) sale.set_number([sale]) ShopTable.write([table], { 'state': 'occupied', 'sale': sale.id, }) return {'status': 'success', 'record': {'id': sale.id}, 'msg': "Orden Comandada exitosamente!"} except Exception as e: print(e, 'error') return {'status': 'error'} else: sale = cls(args['id']) to_add = [] to_write = {} if sale.table_assigned.id != table.id: to_write['table_assigned'] = table.id ShopTable.write([sale.table_assigned], { 'state': 'available', 'sale': None, }) ShopTable.write([table], { 'state': 'occupied', 'sale': sale.id, }) for v in args['lines']: line_id = v.get('id') if line_id < 0: context['price_list'] = sale.shop.price_list to_add.append(cls._set_line(v, context)) try: if to_add: to_write['lines'] = [('create', to_add)] cls.write([sale], to_write) return {'status': 'success', 'record': {'id': sale.id}, 'msg': "Orden Comandada exitosamente!"} except Exception as e: return {'status': 'error', 'record': {'id': sale.id}, 'msg': "Orden no Comandada"} print(e, 'error') @classmethod def dash_get_amount_w_tax(cls, args): Product = Pool().get('product.product') product = Product(args['product']) return product.template.compute_list_price_w_tax( Decimal(args['list_price'])) @classmethod def _get_sales_report(cls, dates, currency_id, in_thousands=False): invoice = Table('account_invoice') period = dates.get('period', None) if period: start_date = period.start_date end_date = period.end_date else: start_date = dates.get('start_date') end_date = dates.get('end_date') cursor = Transaction().connection.cursor() select = invoice.select(Sum(invoice.untaxed_amount_cache), limit=1) select.where = ( invoice.type == 'out') & ( invoice.invoice_date >= start_date) & ( invoice.currency == currency_id) & ( invoice.invoice_date <= end_date) & ( invoice.state.in_(['validated', 'posted', 'paid']) ) cursor.execute(*select) values = cursor.fetchone() value = 0 if values and values[0]: value = int(values[0]) if in_thousands: value = int(value / 1000) return value @classmethod def report_sales_month(cls, args, ctx=None): # Dash Report pool = Pool() Period = pool.get('account.period') Currency = pool.get('currency.currency') res = {} today = date.today() _date = today moment = args.get('moment', None) if moment == 'previous': _date = today - timedelta(days=30) periods = Period.search([ ('start_date', '<=', _date), ('end_date', '>=', _date), ('type', '=', 'standard') ]) if not periods: return res period = periods[0] selector_periods = Period.search([ ('fiscalyear', '=', period.fiscalyear.id), ('type', '=', 'standard') ]) selector = {p.id: p.name for p in selector_periods} currency_id = Transaction().context.get('currency') if args.get('currency'): currency_id = ctx.get('currency') currency = Currency(currency_id) dates = {'period': period} value = cls._get_sales_report(dates, currency_id, in_thousands=True) month_name = _date.strftime("%b %Y") res = { 'value': value, 'selector': selector, 'header_meta': month_name, 'desc': 'In thousands', 'desc_meta': currency.code, } return res @classmethod def report_sales_by_month(cls, args, ctx=None): # Dash Report pool = Pool() Fiscalyear = pool.get('account.fiscalyear') Currency = pool.get('currency.currency') today = date.today() moment = args.get('moment', None) _date = today if moment == 'previous': _date = today - timedelta(days=365) fiscalyear, = Fiscalyear.search([ ('start_date', '<=', _date), ('end_date', '>=', _date), ], limit=1) if not fiscalyear: return {} periods = [p for p in fiscalyear.periods if p.type == 'standard'] currency_id = Transaction().context.get('currency') if args.get('currency'): currency_id = args.get('currency') currency = Currency(currency_id) values = [] labels = [] for p in periods: month = p.start_date.strftime("%b") labels.append(month) dates = {'period': p} val = cls._get_sales_report(dates, currency_id, True) if not val: val = 0 values.append(val) res = { 'values': values, 'labels': labels, 'header_meta': fiscalyear.name, 'desc': 'In thousands', 'desc_meta': currency.code, } return res @classmethod def report_sales_year(cls, args, ctx=None): # Dash Report pool = Pool() Fiscalyear = pool.get('account.fiscalyear') Currency = pool.get('currency.currency') today = date.today() moment = args.get('moment', None) _date = today if moment == 'previous': _date = today - timedelta(days=365) fiscalyear, = Fiscalyear.search([ ('start_date', '<=', _date), ('end_date', '>=', _date), ], limit=1) if not fiscalyear: return {} selector_fy = Fiscalyear.search([ ('id', '=', fiscalyear.id), ]) selector = {p.id: p.name for p in selector_fy} periods = [p for p in fiscalyear.periods if p.type == 'standard'] currency_id = Transaction().context.get('currency') if args.get('currency'): currency_id = args.get('currency') currency = Currency(currency_id) values = [] labels = [] for p in periods: labels.append(p.name) dates = {'period': p} val = cls._get_sales_report(dates, currency_id, True) if val: values.append(val) res = { 'value': sum(values), 'selector': selector, 'header_meta': fiscalyear.name, 'desc': 'In thousands', 'desc_meta': currency.code, } return res @classmethod def report_sales_day(cls, args, ctx=None): # Dash Report pool = Pool() Currency = pool.get('currency.currency') currency_id = Transaction().context.get('currency') if args.get('currency'): currency_id = args.get('currency') currency = Currency(currency_id) today = date.today() _date = today moment = args.get('moment', None) if moment == 'previous': _date = today - timedelta(days=1) dates = { 'start_date': _date, 'end_date': _date } values = cls._get_sales_report(dates, currency_id, True) res = { 'value': values, 'header_meta': str(_date), 'desc': 'In thousands', 'desc_meta': currency.code, } return res @classmethod def report_fulfillment_goal_year(cls, args, ctx=None): pool = Pool() Fiscalyear = pool.get('account.fiscalyear') Goal = pool.get('goal') today = date.today() _date = today moment = args.get('moment', None) if moment == 'previous': _date = today - timedelta(days=365) fiscalyear, = Fiscalyear.search([ ('start_date', '<=', _date), ('end_date', '>=', _date), ], limit=1) if not fiscalyear: return {} periods = [p for p in fiscalyear.periods if p.type == 'standard'] currency_id = Transaction().context.get('currency') if args.get('currency'): currency_id = args.get('currency') # currency = Currency(currency_id) values = [] labels = [] for p in periods: labels.append(p.name) dates = {'period': p} val = cls._get_sales_report(dates, currency_id, True) if val: values.append(val) sum_values = sum(values) goals = Goal.search([ ('fiscalyear', '=', fiscalyear.id), ('kind', '=', 'sales'), ], limit=1) if not goals: goal_rate = 0 missing = 0 else: goal = goals[0].total_amount / 1000 missing = goal - sum_values goal_rate = int((sum_values / goal) * 100) res = { 'labels': ["Sales", "Missing"], 'values': [sum(values), missing], 'center_label': str(goal_rate) + '%', 'header_meta': fiscalyear.name, } return res class AppDelivery(DashAppBase): 'App Delivery' __name__ = 'dash.app.delivery' company = fields.Many2One('company.company', 'Company', required=True) @classmethod def __setup__(cls): super(AppDelivery, cls).__setup__() @staticmethod def default_company(): return Transaction().context.get('company') or None class AppTakeOrder(DashAppBase): 'App Take Order' __name__ = 'dash.app.take_order' class AppOrderViewer(DashAppBase): 'App Order Viewer' __name__ = 'dash.app.order_viewer' @staticmethod def default_company(): return Transaction().context.get('company') or None class AppSaleOrder(DashAppBase): 'App Sale Order' __name__ = 'dash.app.sale_order' allow_discount = fields.Boolean('Allow Discount') allow_manual_pricing = fields.Boolean('Allow Manual Pricing', help='Allow manual pricing to user') class AppSelfServiceSale(DashAppBase): 'App Self Service Sale' __name__ = 'dash.app.self_service_sale' def validate_app(self, args): return {'status': 'ok'} class AppSaleCallCenter(DashAppBase): 'App Sale Call Center' __name__ = 'dash.app.sale_call_center' class AppSaleTurn(DashAppBase): 'App Sale Turn' __name__ = 'dash.app.sale_turn'