2020-04-15 21:47:31 +02:00
|
|
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
|
|
# this repository contains the full copyright notices and license terms.
|
2020-12-20 20:57:25 +01:00
|
|
|
import math
|
2020-04-15 21:47:31 +02:00
|
|
|
from decimal import Decimal
|
|
|
|
from datetime import datetime, date
|
|
|
|
from itertools import chain
|
|
|
|
|
|
|
|
from sql import Table
|
|
|
|
from trytond.model import ModelView, fields
|
|
|
|
from trytond.pool import PoolMeta, Pool
|
|
|
|
from trytond.transaction import Transaction
|
2021-02-09 17:17:01 +01:00
|
|
|
from trytond.pyson import Bool, Eval, Not
|
2020-04-15 21:47:31 +02:00
|
|
|
from trytond.wizard import (
|
2020-12-26 15:12:46 +01:00
|
|
|
Wizard, StateView, StateAction, StateReport, StateTransition, Button
|
|
|
|
)
|
2020-04-15 21:47:31 +02:00
|
|
|
from trytond.report import Report
|
2021-06-22 20:38:42 +02:00
|
|
|
from trytond.i18n import gettext
|
2021-09-09 16:33:32 +02:00
|
|
|
from .exceptions import (
|
|
|
|
ProductMissingTaxError, ImportSalesError, SaleDeleteError,
|
|
|
|
SaleForceDraftError, SaleDeviceError, DraftStatementError,
|
|
|
|
PartyMissingAccount
|
|
|
|
)
|
2021-06-22 20:38:42 +02:00
|
|
|
from trytond.modules.sale.exceptions import SaleValidationError
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
_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)
|
|
|
|
payments = fields.One2Many('account.statement.line', 'sale', 'Payments')
|
2020-12-09 04:53:04 +01:00
|
|
|
paid_amount = fields.Function(fields.Numeric('Paid Amount', digits=(16, 2)),
|
2020-04-15 21:47:31 +02:00
|
|
|
'get_paid_amount')
|
|
|
|
invoice_number = fields.Char('Invoice Number')
|
|
|
|
invoice_date = fields.Date('Invoice Date')
|
|
|
|
invoice = fields.Many2One('account.invoice', 'Invoice')
|
2021-02-09 17:17:01 +01:00
|
|
|
residual_amount = fields.Function(fields.Numeric('Residual Amount',
|
2021-02-24 04:04:13 +01:00
|
|
|
digits=(16, 2), readonly=True), 'get_residual_amount')
|
2020-04-15 21:47:31 +02:00
|
|
|
sale_device = fields.Many2One('sale.device', 'Sale Device',
|
2021-02-24 04:04:13 +01:00
|
|
|
domain=[('shop', '=', Eval('shop'))], depends=['shop'], states=STATES)
|
2020-04-15 21:47:31 +02:00
|
|
|
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)
|
2021-01-04 14:11:58 +01:00
|
|
|
turn = fields.Integer('Turn', states=STATES)
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
@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')
|
2021-02-23 18:40:48 +01:00
|
|
|
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)
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
cls._buttons.update({
|
|
|
|
'wizard_sale_payment': {
|
|
|
|
'invisible': Eval('state') == 'done',
|
|
|
|
'readonly': Not(Bool(Eval('lines'))),
|
|
|
|
},
|
|
|
|
'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
|
|
|
|
|
2021-09-16 19:27:21 +02:00
|
|
|
# @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
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def process(cls, sales):
|
|
|
|
for sale in sales:
|
2021-09-14 00:50:25 +02:00
|
|
|
if sale.invoices:
|
|
|
|
invoice = sale.invoices[0]
|
|
|
|
if invoice.state in ('posted', 'paid'):
|
|
|
|
continue
|
2021-09-13 19:09:38 +02:00
|
|
|
# cls.recheck_taxes(sale)
|
2020-04-15 21:47:31 +02:00
|
|
|
cls.process_pos(sale)
|
|
|
|
super(Sale, cls).process(sales)
|
|
|
|
|
2021-09-13 19:09:38 +02:00
|
|
|
# @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))
|
2021-01-14 01:21:41 +01:00
|
|
|
|
2020-12-09 04:53:04 +01:00
|
|
|
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:
|
2020-12-10 14:37:54 +01:00
|
|
|
if not l.product or (not hasattr(l.product, 'extra_tax') or not l.product.extra_tax):
|
2020-12-09 04:53:04 +01:00
|
|
|
continue
|
2021-01-13 18:29:40 +01:00
|
|
|
raw_val = Decimal(float(l.product.extra_tax) * l.quantity)
|
|
|
|
val = self.currency.round(raw_val)
|
2020-12-09 04:53:04 +01:00
|
|
|
amounts.append(val)
|
|
|
|
return res + sum(amounts)
|
|
|
|
|
2021-12-01 17:38:31 +01:00
|
|
|
# @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
|
2021-03-31 21:33:38 +02:00
|
|
|
|
2020-12-20 20:57:25 +01:00
|
|
|
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
|
|
|
|
|
2021-01-13 14:16:24 +01:00
|
|
|
sale = sales[0]
|
2020-12-20 20:57:25 +01:00
|
|
|
invoice = self._get_invoice_sale()
|
2021-01-13 14:16:24 +01:00
|
|
|
if self.invoice_type:
|
|
|
|
invoice.authorization = self._get_authorization(sale)
|
|
|
|
invoice.invoice_type = self.invoice_type
|
2020-12-20 20:57:25 +01:00
|
|
|
if getattr(invoice, 'lines', None):
|
|
|
|
invoice_lines = list(invoice.lines) + invoice_lines
|
|
|
|
invoice.lines = invoice_lines
|
2021-01-14 01:21:41 +01:00
|
|
|
invoice.turn = self._get_turn()
|
2020-12-20 20:57:25 +01:00
|
|
|
invoice.save()
|
|
|
|
Invoice.update_taxes([invoice])
|
|
|
|
return invoice
|
|
|
|
|
2021-01-13 14:16:24 +01:00
|
|
|
def _get_authorization(self, sale):
|
|
|
|
authorization_id = None
|
2021-03-18 23:36:59 +01:00
|
|
|
if sale.untaxed_amount_cache >= 0:
|
2021-01-13 14:16:24 +01:00
|
|
|
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
|
2021-01-13 18:53:05 +01:00
|
|
|
elif sale.invoice_type == 'C' and sale.shop.computer_authorization:
|
|
|
|
authorization_id = sale.shop.computer_authorization.id
|
2021-01-13 14:16:24 +01:00
|
|
|
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
|
|
|
|
|
2020-04-15 21:47:31 +02:00
|
|
|
@classmethod
|
|
|
|
def process_pos(cls, sale):
|
2021-09-20 18:51:23 +02:00
|
|
|
if not sale:
|
|
|
|
return
|
|
|
|
|
2021-09-14 00:50:25 +02:00
|
|
|
if sale.invoices:
|
|
|
|
invoice = sale.invoices[0]
|
|
|
|
if invoice.number and invoice.state != 'draft':
|
2021-09-20 18:51:23 +02:00
|
|
|
return sale
|
2020-04-15 21:47:31 +02:00
|
|
|
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()
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.get_sequence(sale)
|
2020-04-15 21:47:31 +02:00
|
|
|
if not sale.invoice_number and sale.invoice_method == 'order':
|
2021-08-07 18:06:56 +02:00
|
|
|
if sequence:
|
2021-07-21 19:33:39 +02:00
|
|
|
number = sequence.get()
|
2020-04-15 21:47:31 +02:00
|
|
|
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
|
2021-04-06 14:46:38 +02:00
|
|
|
elif sale.shipment_date:
|
|
|
|
inv_date = sale.shipment_date
|
2020-04-15 21:47:31 +02:00
|
|
|
else:
|
|
|
|
inv_date = Date.today()
|
|
|
|
|
|
|
|
sale.invoice = invoice.id
|
2020-12-20 20:57:25 +01:00
|
|
|
to_write = {
|
2020-04-15 21:47:31 +02:00
|
|
|
'shop': sale.shop.id,
|
|
|
|
'invoice_date': inv_date,
|
|
|
|
'number': number,
|
|
|
|
'reference': sale.reference or sale.number,
|
|
|
|
'position': position,
|
2021-01-04 14:11:58 +01:00
|
|
|
'turn': sale.turn,
|
2020-12-20 20:57:25 +01:00
|
|
|
}
|
|
|
|
|
2020-04-15 21:47:31 +02:00
|
|
|
if sale.invoice_type:
|
2021-01-13 14:16:24 +01:00
|
|
|
pass
|
2020-12-20 20:57:25 +01:00
|
|
|
invoice.write([invoice], to_write)
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2021-02-09 17:17:01 +01:00
|
|
|
@fields.depends('invoice_method', 'pre_sale')
|
2020-04-15 21:47:31 +02:00
|
|
|
def on_change_pre_sale(self):
|
2021-01-04 14:11:58 +01:00
|
|
|
if self.pre_sale:
|
2020-04-15 21:47:31 +02:00
|
|
|
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:
|
2021-06-22 20:38:42 +02:00
|
|
|
raise SaleDeleteError(
|
|
|
|
gettext('sale_pos.msg_delete_invoice_number', sale=sale.rec_name, s=sale.invoice_number))
|
2020-04-15 21:47:31 +02:00
|
|
|
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
|
2021-02-20 16:25:06 +01:00
|
|
|
# 'state': 'confirmed',
|
2020-04-15 21:47:31 +02:00
|
|
|
cls.write([sale], {
|
2021-02-20 16:25:06 +01:00
|
|
|
'state': 'processing',
|
2020-04-15 21:47:31 +02:00
|
|
|
'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:
|
2021-05-18 17:27:56 +02:00
|
|
|
if sale.shipment_state == "none":
|
|
|
|
sale.create_shipment('out')
|
|
|
|
sale.set_shipment_state()
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
@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:
|
2020-04-24 15:45:22 +02:00
|
|
|
if invoice.payment_term.id != sale.payment_term.id:
|
|
|
|
invoice.payment_term = sale.payment_term
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
_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()
|
2021-01-29 18:16:33 +01:00
|
|
|
if invoice.invoice_type not in ('P', 'M', 'C'):
|
2020-04-15 21:47:31 +02:00
|
|
|
Invoice.validate_invoice([invoice])
|
|
|
|
if hasattr(Invoice, 'submit'):
|
|
|
|
Invoice.submit([invoice])
|
2021-04-30 00:47:00 +02:00
|
|
|
if invoice.electronic_state == 'authorized':
|
|
|
|
Invoice.send_email([invoice])
|
2020-04-15 21:47:31 +02:00
|
|
|
|
2021-01-29 17:36:03 +01:00
|
|
|
if invoice.invoice_type in ('P', 'M', 'C') or (
|
2020-04-15 21:47:31 +02:00
|
|
|
hasattr(invoice, 'cufe') and invoice.cufe):
|
2021-04-13 18:51:42 +02:00
|
|
|
if hasattr(sale.shop, 'workflow_invoice') \
|
|
|
|
and sale.shop.workflow_invoice == 'validated':
|
2020-04-15 21:47:31 +02:00
|
|
|
Invoice.write([invoice], {'state': 'validated'})
|
|
|
|
else:
|
2021-09-18 22:18:15 +02:00
|
|
|
sale_ = cls.process_pos(sale)
|
|
|
|
if sale_:
|
|
|
|
sale = sale_
|
2020-04-15 21:47:31 +02:00
|
|
|
if sale.invoice_number:
|
|
|
|
invoice.number = sale.invoice_number
|
|
|
|
else:
|
2021-03-31 21:33:38 +02:00
|
|
|
turn = sale._get_turn()
|
|
|
|
sale.turn = turn
|
|
|
|
invoice.turn = turn
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.get_sequence(sale)
|
2021-09-14 00:50:25 +02:00
|
|
|
|
2021-07-21 19:33:39 +02:00
|
|
|
if sequence:
|
|
|
|
number = sequence.get()
|
2020-04-15 21:47:31 +02:00
|
|
|
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,
|
|
|
|
}])
|
2020-12-20 20:57:25 +01:00
|
|
|
sale.write([sale], {'state': 'done'})
|
2020-04-15 21:47:31 +02:00
|
|
|
except Exception:
|
|
|
|
print('Warning: Sale number not processed %s' % sale.number)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def workflow_to_end(cls, sales):
|
2021-09-18 22:26:15 +02:00
|
|
|
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])
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_paid_amount(cls, sales, names):
|
|
|
|
result = {n: {s.id: Decimal(0) for s in sales} for n in names}
|
|
|
|
for name in names:
|
|
|
|
for sale in sales:
|
|
|
|
for payment in sale.payments:
|
|
|
|
result[name][sale.id] += payment.amount
|
|
|
|
result[name][sale.id] += sale.get_total_vouchers_amount()
|
|
|
|
return result
|
|
|
|
|
2021-01-04 13:50:22 +01:00
|
|
|
def get_residual_amount(self, name=None):
|
2021-11-18 20:55:26 +01:00
|
|
|
total = self.total_amount_cache
|
|
|
|
if not total:
|
|
|
|
total = self.total_amount
|
2021-03-03 17:54:55 +01:00
|
|
|
return (total - self.paid_amount)
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ModelView.button_action('sale_pos.wizard_sale_payment')
|
|
|
|
def wizard_sale_payment(cls, sales):
|
|
|
|
pass
|
|
|
|
|
|
|
|
@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):
|
2021-09-14 00:50:25 +02:00
|
|
|
print('sequence ------ function')
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = None
|
2021-03-18 23:36:59 +01:00
|
|
|
if sale.untaxed_amount_cache >= 0:
|
2020-04-15 21:47:31 +02:00
|
|
|
if sale.invoice_type == 'C' and sale.shop.computer_authorization:
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.shop.computer_authorization.sequence
|
2020-04-15 21:47:31 +02:00
|
|
|
elif sale.invoice_type == 'P' and sale.shop.pos_authorization:
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.shop.pos_authorization.sequence
|
2020-04-15 21:47:31 +02:00
|
|
|
elif sale.invoice_type == 'M' and sale.shop.manual_authorization:
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.shop.manual_authorization.sequence
|
2020-04-15 21:47:31 +02:00
|
|
|
elif sale.invoice_type in ['1','2','3'] and sale.shop.electronic_authorization:
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.shop.electronic_authorization.sequence
|
2020-04-15 21:47:31 +02:00
|
|
|
elif sale.shop.invoice_sequence:
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.shop.invoice_sequence
|
2020-04-15 21:47:31 +02:00
|
|
|
else:
|
|
|
|
if sale.shop.credit_note_electronic_authorization and sale.invoice_type in ['91', 'N']:
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.shop.credit_note_electronic_authorization.sequence
|
2020-04-15 21:47:31 +02:00
|
|
|
elif sale.shop.debit_note_electronic_authorization and sale.invoice_type == '92':
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.shop.debit_note_electronic_authorization.sequence
|
2020-04-15 21:47:31 +02:00
|
|
|
else:
|
|
|
|
if sale.shop.credit_note_sequence:
|
2021-07-21 19:33:39 +02:00
|
|
|
sequence = sale.shop.credit_note_sequence
|
|
|
|
return sequence
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
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:
|
2021-02-09 17:17:01 +01:00
|
|
|
moves[m].state = 'draft'
|
2020-04-15 21:47:31 +02:00
|
|
|
to_create.append(moves[m]._save_values)
|
|
|
|
|
|
|
|
Move.create(to_create)
|
|
|
|
Move.do(self.moves)
|
|
|
|
self.set_shipment_state()
|
|
|
|
|
2021-02-09 17:17:01 +01:00
|
|
|
# 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)
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-02-09 17:17:01 +01:00
|
|
|
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
|
|
|
|
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
class SaleLine(metaclass=PoolMeta):
|
|
|
|
__name__ = 'sale.line'
|
|
|
|
|
2021-02-09 17:17:01 +01:00
|
|
|
# @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
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
@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
|
|
|
|
|
2021-02-09 17:17:01 +01:00
|
|
|
# @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()
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
@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
|
|
|
|
|
2021-11-27 16:08:09 +01:00
|
|
|
def compute_price_w_tax(self, line):
|
|
|
|
Tax = Pool().get('account.tax')
|
|
|
|
res = line.unit_price * Decimal(line.quantity)
|
2021-11-27 16:18:57 +01:00
|
|
|
|
2021-11-27 16:08:09 +01:00
|
|
|
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)
|
2021-11-27 16:18:57 +01:00
|
|
|
if line.product.extra_tax:
|
|
|
|
res += line.product.extra_tax * Decimal(line.quantity)
|
2021-11-27 16:08:09 +01:00
|
|
|
res = res.quantize(
|
|
|
|
Decimal(1) / 10 ** self.__class__.unit_price.digits[1])
|
|
|
|
return res
|
|
|
|
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
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')
|
2021-11-19 23:25:59 +01:00
|
|
|
MoveLine = Pool().get('account.move.line')
|
2020-04-15 21:47:31 +02:00
|
|
|
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)
|
2021-09-08 16:49:16 +02:00
|
|
|
pay.invoice = None
|
|
|
|
pay.save()
|
2020-04-15 21:47:31 +02:00
|
|
|
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':
|
2021-06-22 20:38:42 +02:00
|
|
|
raise SaleForceDraftError(
|
|
|
|
gettext('sale_pos.msg_with_electronic_invoice'))
|
2021-11-19 23:25:59 +01:00
|
|
|
if invoice.move:
|
|
|
|
move = invoice.move
|
|
|
|
MoveLine.check_journal_period_modify(move.period, move.journal)
|
2020-04-15 21:47:31 +02:00
|
|
|
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(
|
2021-05-18 17:27:56 +02:00
|
|
|
columns=[sale_table.state, sale_table.shipment_state, sale_table.invoice_state],
|
|
|
|
values=['draft', 'none', 'none'],
|
2020-04-15 21:47:31 +02:00
|
|
|
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),
|
2021-02-17 19:38:45 +01:00
|
|
|
])
|
2020-04-15 21:47:31 +02:00
|
|
|
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
|
2021-06-22 20:38:42 +02:00
|
|
|
def get_context(cls, records, header, data):
|
2021-09-18 07:12:15 +02:00
|
|
|
from operator import itemgetter
|
2021-06-22 20:38:42 +02:00
|
|
|
report_context = super().get_context(records, header, data)
|
2020-04-15 21:47:31 +02:00
|
|
|
pool = Pool()
|
2021-09-18 07:12:15 +02:00
|
|
|
Invoice = pool.get('account.invoice')
|
2021-02-17 19:38:45 +01:00
|
|
|
InvoiceLine = pool.get('account.invoice.line')
|
2020-04-15 21:47:31 +02:00
|
|
|
Company = pool.get('company.company')
|
2021-09-18 07:12:15 +02:00
|
|
|
Product = pool.get('product.product')
|
2020-04-15 21:47:31 +02:00
|
|
|
|
2021-09-18 07:12:15 +02:00
|
|
|
invoice_dom = [
|
|
|
|
('invoice_date', '>=', data['start_date']),
|
|
|
|
('invoice_date', '<=', data['end_date']),
|
|
|
|
('company', '=', data['company']),
|
|
|
|
('type', '=', 'out'),
|
|
|
|
('state', 'in', ['posted', 'paid', 'validated']),
|
2020-04-15 21:47:31 +02:00
|
|
|
]
|
2021-09-18 07:12:15 +02:00
|
|
|
|
2020-04-15 21:47:31 +02:00
|
|
|
if data['salesman']:
|
2021-09-18 07:12:15 +02:00
|
|
|
invoice_dom.append(
|
|
|
|
('salesman', '=', data['salesman'])
|
2020-04-15 21:47:31 +02:00
|
|
|
)
|
|
|
|
if data['shop']:
|
2021-09-18 07:12:15 +02:00
|
|
|
invoice_dom.append(
|
|
|
|
('shop', '=', data['shop'])
|
2020-04-15 21:47:31 +02:00
|
|
|
)
|
|
|
|
if data['party']:
|
2021-09-18 07:12:15 +02:00
|
|
|
invoice_dom.append(
|
|
|
|
('party', '=', data['party'])
|
2021-02-17 19:38:45 +01:00
|
|
|
)
|
2020-04-15 21:47:31 +02:00
|
|
|
|
2021-09-18 07:12:15 +02:00
|
|
|
fields_inv = [
|
|
|
|
'id', 'number', 'invoice_date', 'state', 'payment_term.name',
|
|
|
|
'shop.name', 'party.name'
|
|
|
|
]
|
2021-02-17 19:38:45 +01:00
|
|
|
|
2021-09-18 07:12:15 +02:00
|
|
|
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'
|
|
|
|
]
|
2020-04-15 21:47:31 +02:00
|
|
|
|
2021-09-18 07:12:15 +02:00
|
|
|
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
|
2021-09-19 09:02:17 +02:00
|
|
|
dgetter = itemgetter(
|
|
|
|
'invoice', 'unit_price', 'quantity', 'product.', 'taxes'
|
|
|
|
)
|
|
|
|
pt_getter = itemgetter(
|
|
|
|
'id', 'name', 'cost_price', 'last_cost', 'account_category.'
|
|
|
|
)
|
2021-09-18 07:12:15 +02:00
|
|
|
for line in lines:
|
|
|
|
invoice, unit_price, qty, product, taxes = dgetter(line)
|
2021-09-19 09:02:17 +02:00
|
|
|
product_id, name, cost_price, last_cost, category = pt_getter(product)
|
2021-09-18 07:12:15 +02:00
|
|
|
line.update(invoices_ids[invoice])
|
2021-09-19 09:02:17 +02:00
|
|
|
cost_price = cost_price or last_cost or 0
|
2021-09-18 07:12:15 +02:00
|
|
|
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
|
2021-09-19 09:02:17 +02:00
|
|
|
|
|
|
|
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 '',
|
|
|
|
})
|
2021-09-18 07:12:15 +02:00
|
|
|
untaxed_amount_append(amount)
|
|
|
|
total_amount_append(amount_taxed)
|
|
|
|
report_context['records'] = lines
|
2021-02-17 19:38:45 +01:00
|
|
|
report_context['untaxed_amount'] = sum(untaxed_amount)
|
2021-09-18 07:12:15 +02:00
|
|
|
report_context['total_amount'] = sum(total_amount)
|
2020-04-15 21:47:31 +02:00
|
|
|
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:
|
2021-06-22 20:38:42 +02:00
|
|
|
raise SaleDeviceError(gettext('sale_pos.msg_not_sale_device'))
|
2020-04-15 21:47:31 +02:00
|
|
|
return {
|
|
|
|
'journal': sale_device.journal.id
|
|
|
|
if sale_device.journal else None,
|
|
|
|
'journals': [j.id for j in sale_device.journals],
|
2021-12-01 23:57:34 +01:00
|
|
|
'payment_amount': abs(sale.total_amount - sale.paid_amount
|
|
|
|
if sale.paid_amount else sale.total_amount),
|
2020-04-15 21:47:31 +02:00
|
|
|
'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
|
2021-12-01 23:57:34 +01:00
|
|
|
if form.payment_amount > 0:
|
2021-11-15 16:28:29 +01:00
|
|
|
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:
|
2021-12-01 23:57:34 +01:00
|
|
|
amount = form.payment_amount
|
|
|
|
if sale.total_amount < 0:
|
|
|
|
amount = amount * -1
|
2021-11-15 16:28:29 +01:00
|
|
|
payment = StatementLine(
|
|
|
|
statement=statements[0].id,
|
|
|
|
date=date.today(),
|
2021-12-02 21:00:09 +01:00
|
|
|
amount=amount,
|
2021-11-15 16:28:29 +01:00
|
|
|
party=sale.party.id,
|
|
|
|
account=account,
|
|
|
|
description=self.start.voucher,
|
|
|
|
sale=active_id,
|
|
|
|
# number=self.start.voucher,
|
|
|
|
# voucher=self.start.voucher,
|
|
|
|
)
|
|
|
|
payment.save()
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
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)
|
2020-07-04 20:56:48 +02:00
|
|
|
shop = fields.Many2One('sale.shop', 'Shop')
|
2020-07-07 16:18:55 +02:00
|
|
|
user = fields.Many2One('res.user', 'User')
|
2020-04-15 21:47:31 +02:00
|
|
|
# journal = fields.Many2One('account.statement.journal', 'Journal')
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_company():
|
|
|
|
return Transaction().context.get('company')
|
|
|
|
|
2020-07-04 15:08:07 +02:00
|
|
|
@staticmethod
|
|
|
|
def default_shop():
|
|
|
|
return Transaction().context.get('shop')
|
|
|
|
|
2020-07-07 16:18:55 +02:00
|
|
|
@staticmethod
|
|
|
|
def default_user():
|
|
|
|
return Transaction().user
|
|
|
|
|
2020-04-15 21:47:31 +02:00
|
|
|
@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):
|
2020-07-07 16:18:55 +02:00
|
|
|
shop_id, user_id = None, None
|
|
|
|
if self.start.user:
|
|
|
|
user_id = self.start.user.id
|
2020-04-15 21:47:31 +02:00
|
|
|
if self.start.shop:
|
|
|
|
shop_id = self.start.shop.id
|
|
|
|
report_context = {
|
|
|
|
'company': self.start.company.id,
|
|
|
|
'date': self.start.date,
|
2020-07-07 16:18:55 +02:00
|
|
|
'user': user_id,
|
2020-04-15 21:47:31 +02:00
|
|
|
'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
|
2021-06-22 20:38:42 +02:00
|
|
|
def get_context(cls, records, header, data):
|
|
|
|
report_context = super().get_context(records, header, data)
|
2020-04-15 21:47:31 +02:00
|
|
|
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')
|
2020-07-15 14:09:28 +02:00
|
|
|
records = []
|
2021-02-16 05:21:40 +01:00
|
|
|
devices = []
|
|
|
|
statements_ = {}
|
2020-07-15 14:09:28 +02:00
|
|
|
advances = []
|
|
|
|
total_advances = []
|
|
|
|
advances_cash = []
|
|
|
|
advances_electronic = []
|
|
|
|
total_statements = []
|
|
|
|
statement_cash = []
|
|
|
|
statement_electronic = []
|
|
|
|
advances_voucher = []
|
|
|
|
total_payments = []
|
|
|
|
payments = []
|
|
|
|
payments_voucher = []
|
|
|
|
payments_cash = []
|
|
|
|
payments_electronic = []
|
2020-04-15 21:47:31 +02:00
|
|
|
|
2020-07-04 20:56:48 +02:00
|
|
|
dom_statement = [
|
2020-04-15 21:47:31 +02:00
|
|
|
('date', '=', data['date']),
|
2020-07-04 20:56:48 +02:00
|
|
|
]
|
|
|
|
if data['shop']:
|
|
|
|
dom_statement.append(('sale_device.shop.id', '=', data['shop']))
|
|
|
|
statements = Statement.search(dom_statement)
|
2020-04-15 21:47:31 +02:00
|
|
|
user_id = Transaction().user
|
|
|
|
user = User(user_id)
|
|
|
|
|
|
|
|
for statement in statements:
|
|
|
|
st_amount = sum(l.amount for l in statement.lines)
|
2021-02-16 05:35:42 +01:00
|
|
|
to_add = {
|
|
|
|
'journal': statement.journal.name,
|
|
|
|
'turn': statement.turn,
|
|
|
|
'total_amount': st_amount,
|
|
|
|
}
|
2021-02-16 05:21:40 +01:00
|
|
|
if statement.sale_device.id not in statements_.keys():
|
2021-02-16 05:35:42 +01:00
|
|
|
statements_[statement.sale_device.id] = {
|
2021-02-16 05:21:40 +01:00
|
|
|
'name': statement.sale_device.name,
|
|
|
|
'total': [st_amount],
|
2021-02-16 05:35:42 +01:00
|
|
|
'records': [to_add]
|
|
|
|
}
|
2021-02-16 05:21:40 +01:00
|
|
|
else:
|
|
|
|
statements_[statement.sale_device.id]['total'].append(st_amount)
|
2021-02-16 05:35:42 +01:00
|
|
|
statements_[statement.sale_device.id]['records'].append(to_add)
|
2020-04-15 21:47:31 +02:00
|
|
|
total_statements.append(st_amount)
|
|
|
|
|
|
|
|
for l in statement.lines:
|
2020-07-10 00:13:04 +02:00
|
|
|
if l.statement.journal.kind == 'cash':
|
|
|
|
statement_cash.append(l.amount)
|
|
|
|
else:
|
|
|
|
statement_electronic.append(l.amount)
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
dom_vouchers = [
|
2020-07-04 15:08:07 +02:00
|
|
|
('move', '!=', None),
|
|
|
|
('move.post_date', '=', data['date']),
|
2020-04-15 21:47:31 +02:00
|
|
|
]
|
2020-07-07 16:18:55 +02:00
|
|
|
if data['user']:
|
|
|
|
dom_vouchers.append(
|
|
|
|
('create_uid', '=', data['user']),
|
|
|
|
)
|
2020-04-15 21:47:31 +02:00
|
|
|
vouchers = Voucher.search(dom_vouchers)
|
|
|
|
for v in vouchers:
|
|
|
|
cash = 0
|
|
|
|
electronic = 0
|
|
|
|
for l in v.lines:
|
2020-07-15 14:09:28 +02:00
|
|
|
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_)
|
2020-04-15 21:47:31 +02:00
|
|
|
|
2020-07-04 20:56:48 +02:00
|
|
|
dom_invoices = [
|
2020-04-15 21:47:31 +02:00
|
|
|
('company', '=', data['company']),
|
|
|
|
('invoice_date', '=', data['date']),
|
|
|
|
('number', '!=', None),
|
2021-09-11 17:19:34 +02:00
|
|
|
# ('state', 'in', ['posted', 'paid', 'canceled', 'validated']),
|
2021-03-03 17:03:35 +01:00
|
|
|
('type', '=', 'out'),
|
2020-04-15 21:47:31 +02:00
|
|
|
]
|
|
|
|
shop_names = ''
|
|
|
|
if data['shop']:
|
|
|
|
shop_names = Shop(data['shop']).name
|
2020-07-04 20:56:48 +02:00
|
|
|
dom_invoices.append(
|
2020-04-15 21:47:31 +02:00
|
|
|
('shop', '=', data['shop'])
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
shops = Shop.search([])
|
|
|
|
for s in shops:
|
|
|
|
shop_names += s.name + ', '
|
|
|
|
|
2020-07-04 20:56:48 +02:00
|
|
|
invoices = Invoice.search(dom_invoices, order=[('number', 'ASC')])
|
2020-04-15 21:47:31 +02:00
|
|
|
invoices_number = []
|
|
|
|
total_invoices_cash = []
|
|
|
|
total_invoices_electronic = []
|
|
|
|
total_invoices_credit = []
|
|
|
|
total_invoices_paid = []
|
2021-02-16 05:21:40 +01:00
|
|
|
devices = {}
|
2020-04-15 21:47:31 +02:00
|
|
|
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)
|
2021-09-11 17:19:34 +02:00
|
|
|
if not invoice.sales:
|
|
|
|
continue
|
2020-04-15 21:47:31 +02:00
|
|
|
sale = invoice.sales[0]
|
2021-02-16 05:21:40 +01:00
|
|
|
device_id = sale.sale_device.id
|
2020-04-15 21:47:31 +02:00
|
|
|
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)
|
2021-02-16 05:21:40 +01:00
|
|
|
paid = cash + electronic
|
2020-04-15 21:47:31 +02:00
|
|
|
inv = {
|
|
|
|
'number': invoice.number,
|
|
|
|
'reference': invoice.reference,
|
|
|
|
'party': invoice.party.name,
|
|
|
|
'total_amount': invoice.total_amount,
|
|
|
|
'credit': credit,
|
|
|
|
'cash': cash,
|
|
|
|
'electronic': electronic,
|
2021-02-16 05:21:40 +01:00
|
|
|
'paid': paid,
|
2020-04-15 21:47:31 +02:00
|
|
|
'state': invoice.state_string,
|
2021-02-13 21:39:09 +01:00
|
|
|
'sale_device': sale.sale_device.name,
|
2020-04-15 21:47:31 +02:00
|
|
|
}
|
|
|
|
records.append(inv)
|
2021-02-16 05:21:40 +01:00
|
|
|
try:
|
|
|
|
devices[device_id]['total'].append(invoice.total_amount)
|
|
|
|
devices[device_id]['cash'].append(cash)
|
|
|
|
devices[device_id]['electronic'].append(electronic)
|
2021-02-16 05:35:42 +01:00
|
|
|
devices[device_id]['credit'].append(credit)
|
2021-02-16 05:21:40 +01:00
|
|
|
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],
|
|
|
|
}
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
advances_cash_ = sum(advances_cash)
|
|
|
|
advances_electronic_ = sum(advances_electronic)
|
|
|
|
statement_cash_ = sum(statement_cash)
|
|
|
|
statement_electronic_ = sum(statement_electronic)
|
2020-07-15 14:09:28 +02:00
|
|
|
payments_cash_ = sum(payments_cash)
|
|
|
|
payments_electronic_ = sum(payments_electronic)
|
2020-04-15 21:47:31 +02:00
|
|
|
|
|
|
|
report_context['records'] = records
|
2021-02-16 05:21:40 +01:00
|
|
|
report_context['devices'] = devices.values()
|
2020-04-15 21:47:31 +02:00
|
|
|
report_context['advances'] = advances
|
2021-02-16 05:35:42 +01:00
|
|
|
report_context['statements'] = statements_.values()
|
2020-04-15 21:47:31 +02:00
|
|
|
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_
|
2020-07-15 14:09:28 +02:00
|
|
|
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
|
2020-04-15 21:47:31 +02:00
|
|
|
return report_context
|
2020-04-22 01:18:59 +02:00
|
|
|
|
|
|
|
|
2020-12-28 03:32:05 +01:00
|
|
|
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)
|
2022-01-12 15:40:53 +01:00
|
|
|
start_time = fields.Time('Start Time')
|
|
|
|
end_time = fields.Time('End Time')
|
2020-12-28 03:32:05 +01:00
|
|
|
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,
|
2022-01-12 15:40:53 +01:00
|
|
|
'start_time': self.start.start_time,
|
|
|
|
'end_time': self.start.end_time,
|
2020-12-28 03:32:05 +01:00
|
|
|
'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
|
2021-06-22 20:38:42 +02:00
|
|
|
def get_context(cls, records, header, data):
|
|
|
|
report_context = super().get_context(records, header, data)
|
2020-12-28 03:32:05 +01:00
|
|
|
pool = Pool()
|
|
|
|
Sale = pool.get('sale.sale')
|
|
|
|
Company = pool.get('company.company')
|
|
|
|
company = Company(data['company'])
|
|
|
|
|
|
|
|
dom = [
|
|
|
|
('company', '=', company),
|
|
|
|
('sale_date', '>=', data['start_date']),
|
|
|
|
('sale_date', '<=', data['end_date']),
|
2020-12-28 04:21:31 +01:00
|
|
|
('state', 'in', ('done', 'processing')),
|
|
|
|
|
2020-12-28 03:32:05 +01:00
|
|
|
]
|
|
|
|
if data['shop']:
|
|
|
|
dom.append(('shop', '=', data['shop']))
|
|
|
|
|
|
|
|
records = {}
|
|
|
|
sales = Sale.search(dom)
|
|
|
|
for sale in sales:
|
|
|
|
if sale.kind:
|
|
|
|
kind = sale.kind
|
|
|
|
else:
|
2020-12-28 04:21:31 +01:00
|
|
|
kind = 'others'
|
2020-12-28 03:32:05 +01:00
|
|
|
if sale.shop:
|
|
|
|
shop = sale.shop.name
|
|
|
|
else:
|
2020-12-28 04:21:31 +01:00
|
|
|
shop = 'others'
|
2020-12-28 03:32:05 +01:00
|
|
|
shop_id = str(shop)
|
2022-01-04 17:01:41 +01:00
|
|
|
untaxed_amount_cache = sale.untaxed_amount_cache
|
|
|
|
# amount_cache = sale.total_amount_cache
|
2020-12-28 03:32:05 +01:00
|
|
|
if shop_id not in records:
|
|
|
|
records[shop_id] = {
|
|
|
|
'shop': shop,
|
2020-12-28 04:21:31 +01:00
|
|
|
'kind_others': [],
|
2020-12-28 03:32:05 +01:00
|
|
|
'kind_to_table': [],
|
|
|
|
'kind_delivery': [],
|
2021-04-17 15:39:18 +02:00
|
|
|
'kind_catering': [],
|
2020-12-28 03:32:05 +01:00
|
|
|
'kind_take_away': [],
|
2020-12-30 00:14:00 +01:00
|
|
|
'sale_amount': []
|
2020-12-28 03:32:05 +01:00
|
|
|
}
|
2020-12-28 04:21:31 +01:00
|
|
|
if kind == 'others':
|
2022-01-04 17:01:41 +01:00
|
|
|
records[shop_id]['kind_others'].append(untaxed_amount_cache)
|
2020-12-28 04:21:31 +01:00
|
|
|
elif kind == 'to_table':
|
2022-01-04 17:01:41 +01:00
|
|
|
records[shop_id]['kind_to_table'].append(untaxed_amount_cache)
|
2020-12-28 04:21:31 +01:00
|
|
|
elif kind == 'delivery':
|
2022-01-04 17:01:41 +01:00
|
|
|
records[shop_id]['kind_delivery'].append(untaxed_amount_cache)
|
2020-12-28 04:21:31 +01:00
|
|
|
elif kind == 'take_away':
|
2022-01-04 17:01:41 +01:00
|
|
|
records[shop_id]['kind_take_away'].append(untaxed_amount_cache)
|
2021-04-17 15:39:18 +02:00
|
|
|
elif kind == 'catering':
|
2022-01-04 17:01:41 +01:00
|
|
|
records[shop_id]['kind_catering'].append(untaxed_amount_cache)
|
|
|
|
records[shop_id]["sale_amount"].append(untaxed_amount_cache)
|
2020-12-28 04:21:31 +01:00
|
|
|
total_amount = 0
|
|
|
|
total_amount_delivery = 0
|
|
|
|
total_amount_others = 0
|
|
|
|
total_amount_take_away = 0
|
|
|
|
total_amount_to_table = 0
|
2021-04-17 15:39:18 +02:00
|
|
|
total_amount_catering = 0
|
2020-12-28 04:21:31 +01:00
|
|
|
total_sales = 0
|
|
|
|
total_sales_delivery = 0
|
|
|
|
total_sales_others = 0
|
|
|
|
total_sales_take_away = 0
|
|
|
|
total_sales_to_table = 0
|
2021-04-17 15:39:18 +02:00
|
|
|
total_sales_catering = 0
|
2020-12-28 03:32:05 +01:00
|
|
|
for r in records.values():
|
2020-12-28 04:21:31 +01:00
|
|
|
total_amount_others += sum(r['kind_others'])
|
|
|
|
total_sales_others += len(r['kind_others'])
|
2020-12-28 03:32:05 +01:00
|
|
|
total_amount_to_table += sum(r['kind_to_table'])
|
|
|
|
total_sales_to_table += len(r['kind_to_table'])
|
|
|
|
total_amount_delivery += sum(r['kind_delivery'])
|
|
|
|
total_sales_delivery += len(r['kind_delivery'])
|
|
|
|
total_amount_take_away += sum(r['kind_take_away'])
|
|
|
|
total_sales_take_away += len(r['kind_take_away'])
|
2021-04-17 15:39:18 +02:00
|
|
|
total_amount_catering += sum(r['kind_catering'])
|
|
|
|
total_sales_catering += len(r['kind_catering'])
|
2020-12-28 03:32:05 +01:00
|
|
|
total_amount += sum(r['sale_amount'])
|
|
|
|
total_sales += len(r['sale_amount'])
|
|
|
|
|
|
|
|
report_context['records'] = records.values()
|
|
|
|
report_context['company'] = company.rec_name
|
2020-12-28 04:21:31 +01:00
|
|
|
report_context['total_amount_others'] = total_amount_others
|
|
|
|
report_context['total_sales_others'] = total_sales_others
|
2020-12-28 03:32:05 +01:00
|
|
|
report_context['total_amount_delivery'] = total_amount_delivery
|
|
|
|
report_context['total_sales_delivery'] = total_sales_delivery
|
|
|
|
report_context['total_amount_take_away'] = total_amount_take_away
|
|
|
|
report_context['total_sales_take_away'] = total_sales_take_away
|
|
|
|
report_context['total_amount_to_table'] = total_amount_to_table
|
|
|
|
report_context['total_sales_to_table'] = total_sales_to_table
|
2021-04-17 15:39:18 +02:00
|
|
|
report_context['total_amount_catering'] = total_amount_catering
|
|
|
|
report_context['total_sales_catering'] = total_sales_catering
|
2020-12-28 03:32:05 +01:00
|
|
|
report_context['total_amount'] = total_amount
|
|
|
|
report_context['total_sales'] = total_sales
|
|
|
|
report_context['data'] = data
|
|
|
|
return report_context
|
|
|
|
|
|
|
|
|
2020-04-22 01:18:59 +02:00
|
|
|
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):
|
2020-08-10 03:33:11 +02:00
|
|
|
cursor = Transaction().connection.cursor()
|
2020-04-22 01:18:59 +02:00
|
|
|
Sale = Pool().get('sale.sale')
|
|
|
|
sales = Sale.search(['AND',
|
2020-12-26 15:12:46 +01:00
|
|
|
['OR', [
|
|
|
|
('state', '=', 'draft'),
|
|
|
|
('invoice_number', '=', ''),
|
|
|
|
], [
|
|
|
|
('state', '=', 'draft'),
|
|
|
|
('invoice_number', '=', None),
|
|
|
|
],
|
|
|
|
]])
|
2020-04-22 01:18:59 +02:00
|
|
|
sales_delete = []
|
|
|
|
for sale in sales:
|
2020-08-10 03:33:11 +02:00
|
|
|
# if sale.lines:
|
|
|
|
# continue
|
2020-04-22 01:18:59 +02:00
|
|
|
if sale.payments:
|
|
|
|
continue
|
2020-08-10 03:33:11 +02:00
|
|
|
sales_delete.append(str(sale.id))
|
|
|
|
# Sale.delete(sales_delete)
|
|
|
|
cursor.execute("DELETE from sale_sale WHERE \
|
2020-12-26 15:12:46 +01:00
|
|
|
id in (%s)" % (', '.join(sales_delete)))
|
2020-04-22 01:18:59 +02:00
|
|
|
return 'end'
|
2020-08-27 21:18:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
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')
|
2021-05-15 15:55:32 +02:00
|
|
|
salesman = fields.Many2One('company.employee', 'Salesman')
|
2021-09-03 16:53:34 +02:00
|
|
|
is_post_date = fields.Boolean('Is Post Date')
|
2020-08-27 21:18:37 +02:00
|
|
|
|
|
|
|
@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,
|
2021-09-03 16:53:34 +02:00
|
|
|
'is_post_date': self.start.is_post_date,
|
2020-08-27 21:18:37 +02:00
|
|
|
}
|
|
|
|
if self.start.user:
|
|
|
|
data['user'] = self.start.user.id
|
2021-05-15 15:55:32 +02:00
|
|
|
|
|
|
|
if self.start.salesman:
|
|
|
|
data['salesman'] = self.start.salesman.id
|
2020-08-27 21:18:37 +02:00
|
|
|
return action, data
|
|
|
|
|
|
|
|
def transition_print_(self):
|
|
|
|
return 'end'
|
|
|
|
|
|
|
|
|
|
|
|
class PortfolioPaymentsReport(Report):
|
|
|
|
'Portfolio Payments Report'
|
|
|
|
__name__ = 'sale_pos.portfolio_payments_report'
|
|
|
|
|
|
|
|
@classmethod
|
2021-06-22 20:38:42 +02:00
|
|
|
def get_context(cls, records, header, data):
|
|
|
|
report_context = super().get_context(records, header, data)
|
2020-08-27 21:18:37 +02:00
|
|
|
pool = Pool()
|
|
|
|
VoucherLine = pool.get('account.voucher.line')
|
|
|
|
today = date.today()
|
|
|
|
dom_search = [
|
2021-09-03 16:53:34 +02:00
|
|
|
|
2020-08-27 21:18:37 +02:00
|
|
|
('voucher.company', '=', data['company']),
|
2021-08-17 17:13:00 +02:00
|
|
|
('voucher.state', '=', 'posted'),
|
2021-08-31 17:37:44 +02:00
|
|
|
('voucher.voucher_type', '=', 'receipt'),
|
2020-08-27 21:18:37 +02:00
|
|
|
]
|
2021-09-03 16:53:34 +02:00
|
|
|
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']), ]
|
|
|
|
)
|
2020-08-27 21:18:37 +02:00
|
|
|
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
|
2021-08-31 17:37:44 +02:00
|
|
|
origin = move_line.move_origin
|
2020-08-27 21:18:37 +02:00
|
|
|
expiration_date = move_line.maturity_date
|
2020-08-27 22:56:02 +02:00
|
|
|
expiration_days = (today - expiration_date).days \
|
|
|
|
if expiration_date else None
|
2020-08-27 21:18:37 +02:00
|
|
|
if origin and origin.__name__ == 'account.invoice':
|
2020-09-01 16:03:16 +02:00
|
|
|
shop = origin.shop.rec_name if origin.shop else ''
|
|
|
|
salesman = origin.salesman.rec_name if origin.salesman else ''
|
2021-05-15 15:55:32 +02:00
|
|
|
salesman_id = origin.salesman.id if origin.salesman else None
|
2020-08-27 21:18:37 +02:00
|
|
|
number = origin.number
|
|
|
|
invoice_date = origin.invoice_date
|
|
|
|
invoice_amount = origin.total_amount
|
|
|
|
invoice_balance = origin.amount_to_pay
|
|
|
|
else:
|
|
|
|
shop = ''
|
|
|
|
salesman = ''
|
2021-08-17 16:57:35 +02:00
|
|
|
salesman_id = ''
|
2020-08-27 21:18:37 +02:00
|
|
|
number = move_line.reference
|
|
|
|
invoice_date = ''
|
|
|
|
invoice_amount = move_line.amount
|
|
|
|
invoice_balance = None
|
|
|
|
|
2021-05-15 15:55:32 +02:00
|
|
|
values = {
|
2020-08-27 21:18:37 +02:00
|
|
|
'number': voucher.number,
|
|
|
|
'shop': shop,
|
|
|
|
'salesman': salesman,
|
2021-05-15 15:55:32 +02:00
|
|
|
'salesman_id': salesman_id,
|
2020-08-27 21:18:37 +02:00
|
|
|
'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,
|
2021-07-20 23:24:07 +02:00
|
|
|
'state': voucher.state_string,
|
2021-05-15 15:55:32 +02:00
|
|
|
}
|
2021-07-20 23:24:07 +02:00
|
|
|
if voucher.state == 'posted':
|
|
|
|
values['post_date'] = voucher.move.post_date
|
2021-05-15 15:55:32 +02:00
|
|
|
if data.get('salesman'):
|
|
|
|
if data['salesman'] == values['salesman_id']:
|
|
|
|
voucher_lines.append(values)
|
|
|
|
else:
|
|
|
|
voucher_lines.append(values)
|
2020-08-27 21:18:37 +02:00
|
|
|
|
|
|
|
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
|
2021-05-18 17:27:56 +02:00
|
|
|
|
|
|
|
|
|
|
|
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:
|
2021-05-18 17:36:11 +02:00
|
|
|
sales = Sale.browse(ids)
|
|
|
|
for sale in sales:
|
2021-05-20 15:38:52 +02:00
|
|
|
if sale.shipment_state != 'none':
|
2021-06-22 20:38:42 +02:00
|
|
|
continue
|
2021-05-20 15:38:52 +02:00
|
|
|
if sale.total_amount_cache < 0:
|
2021-05-18 17:36:11 +02:00
|
|
|
sale.create_shipment('return')
|
|
|
|
sale.set_shipment_state()
|
2021-05-20 15:38:52 +02:00
|
|
|
else:
|
2021-05-18 17:36:11 +02:00
|
|
|
sale.create_shipment('out')
|
|
|
|
sale.set_shipment_state()
|
2021-05-18 17:27:56 +02:00
|
|
|
return 'end'
|
2021-12-20 21:35:30 +01:00
|
|
|
|
|
|
|
|
|
|
|
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'])
|
2022-01-05 16:24:26 +01:00
|
|
|
shop_name = ''
|
|
|
|
if data['shop']:
|
|
|
|
shop = Shop(data['shop'])
|
|
|
|
shop_name = shop.name
|
2021-12-20 21:35:30 +01:00
|
|
|
|
|
|
|
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],
|
|
|
|
}
|
|
|
|
|
2022-01-05 16:20:54 +01:00
|
|
|
report_context['shop'] = shop_name
|
2021-12-20 21:35:30 +01:00
|
|
|
report_context['company'] = company
|
|
|
|
report_context['records'] = records.values()
|
|
|
|
report_context['total_cost'] = total_cost
|
|
|
|
report_context['total_income'] = total_income
|
|
|
|
return report_context
|