# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. from decimal import Decimal from datetime import time, datetime, timedelta from sql import Null from sql.aggregate import Sum from trytond.model import fields, ModelView from trytond.pyson import Eval from trytond.pool import PoolMeta, Pool from trytond.transaction import Transaction from trytond.wizard import Wizard, StateView, Button, StateReport from trytond.report import Report _ZERO = Decimal(0) class SaleShop(metaclass=PoolMeta): __name__ = 'sale.shop' party = fields.Many2One('party.party', 'Default Party') invoice_sequence = fields.Many2One('ir.sequence.strict', 'Invoice Sequence') credit_note_sequence = fields.Many2One('ir.sequence.strict', 'Credit Note Sequence') # self_pick_up = fields.Boolean('Default Self Pick Up', # help='The goods are picked up by the customer before the sale, so no ' # 'shipment is created.') pos_authorization = fields.Many2One('account.invoice.authorization', 'POS Authorization', domain=[ ('kind', '=', 'P'), ('company', '=', Eval('company')), ]) computer_authorization = fields.Many2One('account.invoice.authorization', 'Computer Authorization', domain=[ ('kind', '=', 'C'), ('company', '=', Eval('company')), ]) electronic_authorization = fields.Many2One('account.invoice.authorization', 'Electronic Authorization', domain=[ ('kind', 'in', ['1', '2', '3']), ('company', '=', Eval('company')), ]) credit_note_electronic_authorization = fields.Many2One('account.invoice.authorization', 'Credit Note Electronic Authorization', domain=[ ('kind', '=', '91'), ('company', '=', Eval('company')), ]) debit_note_electronic_authorization = fields.Many2One('account.invoice.authorization', 'Debit Note Electronic Authorization', domain=[ ('kind', '=', '92'), ('company', '=', Eval('company')), ]) manual_authorization = fields.Many2One('account.invoice.authorization', 'Manual Authorization', domain=[ ('kind', '=', 'M'), ('company', '=', Eval('company')), ]) freight_product = fields.Many2One('product.product', 'Freight Product') email_template = fields.Many2One('email.template', 'Template') class ShopDailySummaryStart(ModelView): 'Shop Daily Summary Start' __name__ = 'sale_pos.shop_daily_summary.start' company = fields.Many2One('company.company', 'Company', required=True) sale_date = fields.Date('Sale Date', required=True) shop = fields.Many2One('sale.shop', 'Shop', required=True) early_morning_included = fields.Boolean('Early Morning Included') @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_sale_date(): Date = Pool().get('ir.date') return Date.today() class ShopDailySummary(Wizard): 'Shop Daily Summary' __name__ = 'sale_pos.shop_daily_summary' start = StateView('sale_pos.shop_daily_summary.start', 'sale_pos.shop_daily_summary_start_view_form', [ Button('Cancel', 'end', 'tryton-cancel'), Button('Print', 'print_', 'tryton-ok', default=True), ]) print_ = StateReport('sale_pos.shop_daily_summary_report') def do_print_(self, action): report_context = { 'ids': [], 'company': self.start.company.id, 'sale_date': self.start.sale_date, 'shop': self.start.shop.id, 'early_morning_included': self.start.early_morning_included, } return action, report_context def transition_print_(self): return 'end' class ShopDailySummaryReport(Report): 'Shop Daily Summary' __name__ = 'sale_pos.shop_daily_summary_report' @classmethod def get_query(cls, data, products_exception): pool = Pool() Sale = pool.get('sale.sale') Line = pool.get('sale.line') Invoice = pool.get('account.invoice') Company = pool.get('company.company') fixed_hour = time(6, 0) cursor = Transaction().connection.cursor() sale = Sale.__table__() line = Line.__table__() result_ = {} where_ = sale.state.in_(['processing', 'done']) where_ = line.product.in_(products_exception) where_ &= sale.company == data['company'] where_ &= sale.invoice_type == 'P' where_ &= sale.number != Null if data['shop']: where_ &= sale.shop == data['shop'] if not data['early_morning_included']: where_ &= sale.invoice_date == data['sale_date'] else: # Select sales including early morning of next day _start_date = datetime.combine(data['sale_date'], fixed_hour) _start_date = Company.convert_timezone(_start_date, True) end_date = data['sale_date'] + timedelta(days=1) _end_date = datetime.combine(end_date, fixed_hour) _end_date = Company.convert_timezone(_end_date, True) where_ &= sale.sale_date >= data['sale_date'] where_ &= sale.sale_date <= end_date where_ &= sale.create_date >= _start_date where_ &= sale.create_date <= _end_date columns_ = [sale.id, Sum(line.unit_price*line.quantity).as_('amount')] query = line.join(sale, condition=line.sale==sale.id).select(*columns_, where=where_, group_by=sale.id) cursor.execute(*query) columns = list(cursor.description) result = cursor.fetchall() for row in result: result_[row[0]] = row[1] return result_ @classmethod def get_context(cls, records, header, data): report_context = super().get_context(records, header, data) pool = Pool() Sale = pool.get('sale.sale') Company = pool.get('company.company') company = Company(data['company']) Shop = pool.get('sale.shop') Tax = pool.get('account.tax') Device = pool.get('sale.device') fixed_hour = time(6, 0) config = pool.get('sale.configuration')(1) products_exception = [] amount_exception = {} if hasattr(config, 'tip_product') and config.tip_product and config.exclude_tip_and_delivery: products_exception.append(config.tip_product.id) if hasattr(config, 'delivery_product') and config.delivery_product and config.exclude_tip_and_delivery: products_exception.append(config.delivery_product.id) if products_exception: amount_exception = cls.get_query(data, products_exception) dom_sales = [ ('shop', '=', data['shop']), ('company', '=', data['company']), ('number', '!=', None), ('invoice_type', '=', 'P'), ] if not data['early_morning_included']: dom_sales.append(('sale_date', '=', data['sale_date'])) else: # Select sales including early morning of next day _start_date = datetime.combine(data['sale_date'], fixed_hour) _start_date = Company.convert_timezone(_start_date, True) end_date = data['sale_date'] + timedelta(days=1) _end_date = datetime.combine(end_date, fixed_hour) _end_date = Company.convert_timezone(_end_date, True) dom_sales.append(('sale_date', '>=', data['sale_date'])) dom_sales.append(('sale_date', '<=', end_date)) dom_sales.append(('create_date', '>=', _start_date)) dom_sales.append(('create_date', '<=', _end_date)) states_sale = ['processing', 'done'] dom_sales.append(('state', 'in', states_sale)) sales = Sale.search(dom_sales, order=[('number', 'ASC')]) untaxed_amount = [] tax_amount = [] total_amount = [] devices_ = Device.search([ ('shop', '=', data['shop']) ]) devices = {} for d in devices_: devices[d.id] = { 'name': d.name, 'code': d.code, 'count_invoices': 0, 'untaxed_amount': [], 'tax_amount': [], 'total_amount': [], 'cash': [], 'credit': [], 'electronic': [], 'other': [], } payment_modes = { 'cash': [], 'credit': [], 'electronic': [], 'other': [], } numbers = [] categories = {} discounts = {} _payments = {} total_discount = [] total_payments = [] for sale in sales: payments = sale.payments sale_id = sale.id device_id = None try: value_except = Decimal(amount_exception[sale_id]) except: value_except= Decimal(0) if sale.sale_device: device_id = sale.sale_device.id if sale.total_amount <= 0: continue if not sale.invoices: continue invoice = sale.invoices[0] if not invoice.number or invoice.total_amount <= 0 or not device_id: continue numbers.append(invoice.number) untaxed_ammount_ = invoice.untaxed_amount - value_except total_amount_ = invoice.total_amount - value_except devices[device_id]['count_invoices'] += 1 devices[device_id]['untaxed_amount'].append(untaxed_ammount_) devices[device_id]['tax_amount'].append(invoice.tax_amount) devices[device_id]['total_amount'].append(total_amount_) untaxed_amount.append(untaxed_ammount_) tax_amount.append(invoice.tax_amount) total_amount.append(total_amount_) if payments: amount_by_sale = [] for payment in payments: kind = payment.statement.journal.kind amount = payment.amount if value_except > 0 and payment.amount > value_except: amount = payment.amount - value_except value_except = 0 amount_by_sale.append(amount) if kind not in ['cash', 'credit', 'electronic']: kind = 'other' devices[device_id][kind].append(amount) payment_modes[kind].append(amount) journal = payment.statement.journal try: _payments[journal.id]['amount'].append(amount) except: _payments[journal.id] = { 'name': journal.name, 'amount': [amount], } total_payments.append(amount) amount_to_pay = invoice.amount_to_pay if amount_to_pay > 0: # THIS MUST WORKS IN FUTURE WITH ADD PAYMENT INSTATEMENT TO INVOICE # devices[device_id]['credit'].append(amount_to_pay) # payment_modes['credit'].append(amount_to_pay) # FIX TEMPORAL inv_balance = invoice.total_amount - sum(amount_by_sale) devices[device_id]['credit'].append(inv_balance) payment_modes['credit'].append(inv_balance) else: devices[sale.sale_device.id]['credit'].append(total_amount_) if value_except > 0: payment_modes['credit'].append(invoice.amount_to_pay - value_except) else: payment_modes['credit'].append(invoice.amount_to_pay) for line in invoice.lines: category_id = '0' if line.product.id in products_exception: continue if line.product.account_category: category = line.product.account_category category_id = category.id if category_id not in categories.keys(): categories[category_id] = { 'name': category.name, 'base': [line.amount], 'taxes': {}, } if line.taxes and line.amount: for t in line.taxes: categories[category_id]['taxes'][t.id] = { 'tax': [t], 'base': [line.amount], } else: if line.taxes and line.amount: for t in line.taxes: try: categories[category_id]['taxes'][t.id]['base'].append(line.amount) except: categories[category_id]['taxes'][t.id] = { 'tax': [t], 'base': [line.amount], } categories[category_id]['base'].append(line.amount) if line.discount: try: disc = line.amount / (1 - line.discount) except: disc = line.product.template.list_price * Decimal(line.quantity) if category_id not in discounts.keys(): discounts[category_id] = { 'name': category.name, 'amount': [disc], } else: discounts[category_id]['amount'].append(disc) total_discount.append(disc) for k, v in categories.items(): base = sum(v['base']) categories[k]['base'] = base taxes = categories[k]['taxes'] if len(taxes) > 0: for t, m in categories[k]['taxes'].items(): tax_list = Tax.compute(m['tax'], sum(m['base']), 1) categories[k]['taxes'][t].update({ 'name': tax_list[0]['tax'].name, 'base': sum(m['base']), 'amount': tax_list[0]['amount'] }) else: categories[k]['taxes'][0] = { 'name': 'EXCLUIDOS / EXENTOS', 'base': base, 'amount': _ZERO } if numbers: min_number = min(numbers) max_number = max(numbers) else: min_number = '' max_number = '' report_context['date'] = data['sale_date'] report_context['company'] = company report_context['shop'] = Shop(data['shop']).name report_context['start_number'] = min_number report_context['end_number'] = max_number report_context['records'] = devices.values() report_context['categories'] = categories.values() report_context['sum_count_invoices'] = len(numbers) 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['discounts'] = discounts.values() report_context['total_discount'] = sum(total_discount) report_context['payments'] = _payments.values() report_context['total_payments'] = sum(total_payments) report_context['sum_cash'] = sum(payment_modes['cash']) report_context['sum_credit'] = sum(payment_modes['credit']) report_context['sum_electronic'] = sum(payment_modes['electronic']) report_context['sum_other'] = sum(payment_modes['other']) return report_context class ShopDailyCategoryStart(ModelView): 'Shop Daily Summary Start' __name__ = 'sale_pos.shop_daily_category.start' company = fields.Many2One('company.company', 'Company', required=True) sale_date = fields.Date('Sale Date', required=True) end_date = fields.Date('End Date') shop = fields.Many2One('sale.shop', 'Shop', required=True) early_morning_included = fields.Boolean('Early Morning Included') @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_sale_date(): Date = Pool().get('ir.date') return Date.today() class ShopDailyCategory(Wizard): 'Shop Daily Summary' __name__ = 'sale_pos.shop_daily_category' start = StateView('sale_pos.shop_daily_category.start', 'sale_pos.shop_daily_category_start_view_form', [ Button('Cancel', 'end', 'tryton-cancel'), Button('Print', 'print_', 'tryton-ok', default=True), ]) print_ = StateReport('sale_pos.shop_daily_category_report') def do_print_(self, action): report_context = { 'ids': [], 'company': self.start.company.id, 'sale_date': self.start.sale_date, 'end_date': self.start.end_date, 'shop': self.start.shop.id, 'early_morning_included': self.start.early_morning_included, } return action, report_context def transition_print_(self): return 'end' class ShopDailyCategoryReport(Report): 'Shop Daily categories' __name__ = 'sale_pos.shop_daily_category_report' @classmethod def get_query(cls, data, products_exception): pool = Pool() Sale = pool.get('sale.sale') Line = pool.get('sale.line') Invoice = pool.get('account.invoice') fixed_hour = time(6, 0) cursor = Transaction().connection.cursor() sale = Sale.__table__() line = Line.__table__() result_ = {} where_ = sale.state.in_(['processing', 'done']) where_ = line.product.in_(products_exception) where_ &= sale.company == data['company'] # where_ &= sale.invoice_type == 'P' where_ &= sale.number != Null if data['shop']: where_ &= sale.shop == data['shop'] if not data['early_morning_included']: where_ &= sale.invoice_date == data['sale_date'] else: # Select sales including early morning of next day _start_date = datetime.combine(data['sale_date'], fixed_hour) _start_date = Company.convert_timezone(_start_date, True) end_date = data['sale_date'] + timedelta(days=1) if not data['sale_date'] else data['sale_date'] + timedelta(days=1) _end_date = datetime.combine(end_date, fixed_hour) _end_date = Company.convert_timezone(_end_date, True) where_ &= sale_date >= data['sale_date'] where_ &= sale_date <= end_date where_ &= create_date >= _start_date where_ &= create_date <= _end_date columns_ = [sale.id, Sum(line.unit_price*line.quantity).as_('amount')] query = line.join(sale, condition=line.sale==sale.id).select(*columns_, where=where_, group_by=sale.id) cursor.execute(*query) result = cursor.fetchall() for row in result: result_[row[0]] = row[1] return result_ @classmethod def get_context(cls, records, header, data): report_context = super().get_context(records, header, data) pool = Pool() Sale = pool.get('sale.sale') Company = pool.get('company.company') company = Company(data['company']) Shop = pool.get('sale.shop') fixed_hour = time(6, 0) # config = pool.get('sale.configuration')(1) products_exception = [] amount_exception = {} if products_exception: amount_exception = cls.get_query(data, products_exception) dom_sales = [ ('shop', '=', data['shop']), ('company', '=', data['company']), ('number', '!=', None), # ('invoice_type', '=', 'P'), ] if not data['early_morning_included']: if data['end_date']: dom_sales.append(('sale_date', '>=', data['sale_date'])) dom_sales.append(('sale_date', '<=', data['end_date'])) else: dom_sales.append(('sale_date', '=', data['sale_date'])) else: # Select sales including early morning of next day _start_date = datetime.combine(data['sale_date'], fixed_hour) _start_date = Company.convert_timezone(_start_date, True) end_date = data['sale_date'] + timedelta(days=1) if not data['end_date'] else data['end_date'] + timedelta(days=1) _end_date = datetime.combine(end_date, fixed_hour) _end_date = Company.convert_timezone(_end_date, True) dom_sales.append(('sale_date', '>=', data['sale_date'])) dom_sales.append(('sale_date', '<=', end_date)) dom_sales.append(('create_date', '>=', _start_date)) dom_sales.append(('create_date', '<=', _end_date)) print(dom_sales) states_sale = ['processing', 'done'] dom_sales.append(('state', 'in', states_sale)) sales = Sale.search(dom_sales, order=[('number', 'ASC')]) categories = {} total_payments = [] print('voy a entrar al for sale') for sale in sales: device_id = None if sale.sale_device: device_id = sale.sale_device.id if sale.total_amount <= 0: continue if not sale.invoices: continue invoice = sale.invoices[0] if not invoice.number or invoice.total_amount <= 0 or not device_id: continue for line in sale.lines: category_id = '0' product_id = line.product.id product_name = line.product.name line_quantity = line.quantity line_amount_w_tax = line.amount_w_tax if product_id in products_exception: continue if line.product.categories: category = line.product.categories[0] category_id = category.id try: if category_id != '0': categories[category_id]['base'].append(line.amount) try: categories[category_id]['products'][product_id]['quantity'] += line_quantity categories[category_id]['products'][product_id]['amount'] += line_amount_w_tax total_payments.append(line_amount_w_tax) except KeyError: categories[category_id]['products'][product_id] = { 'name': product_name, 'quantity': line_quantity, 'amount': line_amount_w_tax, } total_payments.append(line_amount_w_tax) except KeyError: categories[category_id] = { 'name': category.name, 'base': [line.amount], 'products': {product_id: { 'name': product_name, 'quantity': line_quantity, 'amount': line_amount_w_tax, } } } total_payments.append(line_amount_w_tax) report_context['date'] = data['sale_date'] report_context['end_date'] = data['end_date'] if data['end_date'] else data['sale_date'] report_context['company'] = company.party report_context['shop'] = Shop(data['shop']).name report_context['categories'] = categories.values() report_context['total_payments'] = sum(total_payments) return report_context