trytonpsk-dash_sale/sale.py

869 lines
28 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.
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']
for t in range(1, 17):
if t == 1:
sleep(20)
response = get_response_pay(terminal)
else:
sleep(10)
response = get_response_pay(terminal)
response_process_pay = process_response(response)
if response_process_pay is not None:
break
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
for journal in device.journals:
if journal.payment_means_code == payment_means_code:
journal_id = journal.id
break
statement_open_id = cls.is_statement_open(
journal_id, sale.sale_device.id)
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])
qr_code = sale.number + ' ' + str(sale.sale_date)
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['qr_code'] = qr_code
for d in data_order[0].values():
d['qr_code'] = qr_code
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'
class AppSaleCallCenter(DashAppBase):
'App Sale Call Center'
__name__ = 'dash.app.sale_call_center'