trytonpsk-sale_pos/shop.py

603 lines
24 KiB
Python

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from 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