update create sale and invoice from order

This commit is contained in:
wilson gomez 2021-07-07 11:44:47 -05:00
parent e9a95b2f03
commit 821ee1316d
16 changed files with 416 additions and 36 deletions

View File

@ -7,6 +7,7 @@ from . import service_order
from . import test_type
from . import party
from . import sale
from . import invoice
def register():
@ -19,8 +20,12 @@ def register():
service_order.Order,
service_order.OrderLine,
service_order.OrderTestResult,
invoice.InvoiceLaboratoryOrder,
invoice.Invoice,
service_order.CreateInvoiceStart,
module='laboratory', type_='model')
Pool.register(
service_order.CreateInvoice,
service_order.PrintServiceOrder,
module='laboratory', type_='wizard')
Pool.register(

View File

@ -2,14 +2,26 @@
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelView, ModelSQL, fields
from trytond.transaction import Transaction
from trytond.pyson import Eval, Id
class Configuration(ModelSQL, ModelView):
'Laboratory Configuration'
__name__ = 'laboratory.configuration'
laboratory_service_order_sequence = fields.Many2One('ir.sequence',
'Service Order Sequence', required=True)
'Service Order Sequence', required=True,
domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('sequence_type', '=',
Id('laboratory',
'sequence_type_laboratory')),
])
company = fields.Many2One('company.company', 'Company', required=True)
co_pay_product = fields.Many2One('product.product', 'Product Co-pay',
domain=[('salable', '=', True)])
delivery_product = fields.Many2One('product.product', 'Product Delivery',
domain=[('salable', '=', True)])
@staticmethod
def default_company():

9
exceptions.py Normal file
View File

@ -0,0 +1,9 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.exceptions import UserError
from trytond.model.exceptions import ValidationError
class ValidatePaymentTermParty(ValidationError):
pass

36
invoice.py Normal file
View File

@ -0,0 +1,36 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql import Table
from decimal import Decimal
from trytond.model import fields, ModelSQL
from trytond.i18n import gettext
from trytond.pool import PoolMeta
# from .exceptions import (
# NotificationAuthError, NotificationAuthWarning, InvalidTypeInvoiceError
# )
# MissingInvoiceTaxError, InvoiceDuplicatedError,
# AuthExpiredError,
_ZERO = Decimal('0.00')
class Invoice(metaclass=PoolMeta):
__name__ = 'account.invoice'
annexes = fields.Many2Many('account.invoice-laboratory.order', 'invoice', 'order', 'annexes')
class InvoiceLine(metaclass=PoolMeta):
__name__ = 'account.invoice.line'
class InvoiceLaboratoryOrder(ModelSQL):
"Invoice - Order"
__name__ = "account.invoice-laboratory.order"
_table = 'account_invoice_laboratory_order'
invoice = fields.Many2One('account.invoice', 'Invoice',
ondelete='CASCADE', select=True, required=True)
order = fields.Many2One('laboratory.order', 'Laboratory Order',
ondelete='RESTRICT', select=True, required=True)

13
invoice.xml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="invoice_view_form">
<field name="model">account.invoice</field>
<field name="inherit" ref="account_invoice.invoice_view_form"/>
<field name="name">invoice_form</field>
</record>
</data>
</tryton>

11
message.xml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data grouped="1">
<record model="ir.message" id="msg_missing_payment_term">
<field name="text">Missing payment term customer for party with document "%s"!</field>
</record>
</data>
</tryton>

View File

@ -11,4 +11,10 @@ STATES = {
class Sale(metaclass=PoolMeta):
__name__ = 'sale.sale'
patient_payment = fields.Numeric('Patient Payment', states=STATES)
# patient_payment = fields.Numeric('Patient Payment', states=STATES)
@classmethod
def _get_origin(cls):
models = super(Sale, cls)._get_origin()
'Return list of Model names for origin Reference'
return models + ['laboratory.order']

View File

@ -6,9 +6,13 @@ from trytond.model import Workflow, ModelView, ModelSQL, fields
from trytond.pyson import Eval, If, In, Get
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.wizard import Wizard, StateReport, StateTransition
from trytond.wizard import Wizard, StateReport, StateTransition, StateView, Button
from trytond.report import Report
from trytond.exceptions import UserError
from trytond.i18n import gettext
from .exceptions import ValidatePaymentTermParty
from trytond.modules.company.model import (
employee_field, set_employee, reset_employee)
STATES = {
'readonly': (Eval('state') != 'draft'),
@ -40,10 +44,11 @@ class Order(Workflow, ModelSQL, ModelView):
patient = fields.Many2One('party.party', 'Patient', states=STATES)
description = fields.Char('Description', states=STATES)
order_date = fields.Date('Order Date', required=True, states=STATES)
patient_payment = fields.Numeric('Patient Payment', states=STATES)
co_pay = fields.Numeric('Co-Pay', states=STATES)
delivery_amount = fields.Numeric('Delivery Amount', states=STATES)
discount_amount = fields.Numeric('Discount Amount', states=STATES)
end_date = fields.Date('End Date', states=STATES_CLOSE, depends=['state'])
received_by = fields.Many2One('company.employee', 'Received By',
states=STATES)
received_by = employee_field("Received By")
company = fields.Many2One('company.company', 'Company', required=True,
states=STATES, domain=[('id', If(In('company',
Eval('context', {})), '=', '!='), Get(Eval('context', {}),
@ -57,6 +62,12 @@ class Order(Workflow, ModelSQL, ModelView):
('cancelled', 'Cancelled'),
], 'State', readonly=True, required=True)
state_string = state.translated('state')
status_invoice = fields.Selection([
('without_invoice', 'Without Invoice'),
('partial_invoice', 'Partial Invoice'),
('invoiced', 'Invoiced'),
], 'Status Invoice', readonly=True, required=True)
state_string = state.translated('state')
price_list = fields.Many2One('product.price_list', 'Price List',
states=STATES_CLOSE)
lines = fields.One2Many('laboratory.order.line', 'order',
@ -64,6 +75,10 @@ class Order(Workflow, ModelSQL, ModelView):
notes = fields.Text('Notes', states=STATES_CLOSE)
payment_term = fields.Many2One('account.invoice.payment_term',
'Payment Term', states=STATES, depends=['state'])
co_pay_invoice = fields.Many2One('account.invoice', 'Co-Pay Invoice', states=STATES,
depends=['state'])
invoice = fields.Many2One('account.invoice', 'Invoice', states=STATES,
depends=['state'])
@classmethod
def __setup__(cls):
@ -102,9 +117,14 @@ class Order(Workflow, ModelSQL, ModelView):
return Transaction().context.get('company') or False
@staticmethod
@reset_employee('approved_by')
def default_state():
return 'draft'
@staticmethod
def default_status_invoice():
return 'without_invoice'
@staticmethod
def default_order_date():
Date = Pool().get('ir.date')
@ -113,6 +133,7 @@ class Order(Workflow, ModelSQL, ModelView):
@classmethod
@ModelView.button
@Workflow.transition('reception')
@set_employee('received_by')
def reception(cls, records):
for record in records:
record.set_number()
@ -134,7 +155,9 @@ class Order(Workflow, ModelSQL, ModelView):
@Workflow.transition('finished')
def finished(cls, records):
for rec in records:
rec.create_sale()
sale = rec.create_sale()
if sale:
rec.process_order(sale)
@classmethod
@ModelView.button
@ -150,9 +173,54 @@ class Order(Workflow, ModelSQL, ModelView):
raise UserError('delete_numbered')
super(Order, cls).delete(records)
def get_status_invoice(self, name):
if self.co_pay and self.co_pay > 0:
if not self.co_pay_invoice and not self.invoice:
status_invoice = 'without_invoice'
elif not self.co_pay_invoice or not self.invoice:
status_invoice = 'partial_invoice'
else:
status_invoice = 'invoiced'
else:
if not self.invoice:
status_invoice = 'without_invoice'
else:
status_invoice = 'invoiced'
return status_invoice
def process_order(self, sale):
pool = Pool()
Date = pool.get('ir.date')
Sale = pool.get('sale.sale')
Invoice = pool.get('account.invoice')
self.end_date = Date.today()
Sale.quote([sale])
Sale.confirm([sale])
Sale.process([sale])
if sale.invoices:
invoice = sale.invoices[0]
invoice.invoice_date = self.end_date
invoice.save()
Invoice.validate_invoice([invoice])
if invoice.type == 'out' and invoice.invoice_type not in ['', None, 'C', 'P', 'M']:
try:
Invoice.submit([invoice])
except Exception as e:
print('WARNING: invoice dont send to dian', e)
Invoice.post([invoice])
if self.co_pay and self.co_pay > 0:
self.co_pay_invoice = invoice.id
self.status_invoice = 'partial_invoice'
else:
self.status_invoice = 'invoiced'
self.invoice = invoice.id
self.save()
def set_number(self):
pool = Pool()
Config = pool.get('laboratory.configuration')
if self.number:
return
config = Config.get_configuration()
if not config.laboratory_service_order_sequence:
raise UserError('missing_sequence_service_order')
@ -165,40 +233,196 @@ class Order(Workflow, ModelSQL, ModelView):
Sale = pool.get('sale.sale')
Party = pool.get('party.party')
patient_id = None
if self.patient:
patient_id = self.patient.id
_lines = []
address = Party.address_get(self.customer, type='invoice')
for line in self.lines:
pto = line.test.product
_lines.append({
'type': 'line',
'unit': pto.template.default_uom.id,
'quantity': line.quantity,
'unit_price': line.unit_price,
'product': pto.id,
'description': pto.rec_name,
})
if self.co_pay and self.co_pay > 0:
address = Party.address_get(self.patient, type='invoice')
payment_term = self.payment_term.id
invoice_party = self.patient.id
party = self.patient.id
_line = self.get_line_order(type='co_pay_product')
_lines.append(_line)
else:
if self.patient:
party = self.patient.id
else:
party = self.customer.id
address = Party.address_get(self.customer, type='invoice')
payment_term = self.payment_term.id
for line in self.lines:
_line = self.get_line_order(line=line)
_lines.append(_line)
if self.delivery_amount and self.delivery_amount > 0:
_line = self.get_line_order(type='delivery_product')
_lines.append(_line)
str_origin = 'laboratory.order,' + str(self.id)
data = {
'company': self.company.id,
'payment_term': self.payment_term.id,
'number': self.number,
'invoice_party': self.customer.id,
'payment_term': payment_term,
'reference': self.number,
'invoice_party': invoice_party,
'invoice_type': 'P',
'currency': 31, #FIXME
'patient_payment': self.patient_payment,
'party': patient_id,
# 'patient_payment': self.patient_payment,
'party': party,
'sale_date': self.order_date,
'state': 'draft',
'invoice_address': address,
'shipment_address': address,
'origin': str_origin,
'lines': [('create', _lines)]
}
sale, = Sale.create([data])
return sale
def get_line_order(self, line=None, type=None):
pool = Pool()
Configuration = pool.get('laboratory.configuration')
config = Configuration(1)
if type and type == 'co_pay_product':
if not config.co_pay_product:
raise UserError('missing_product_co_pay')
product = config.co_pay_product
quantity = 1
unit_price = self.co_pay
elif type and type == 'delivery_product':
if not config.delivery_product:
raise UserError('missing_product_delivey')
product = config.delivery_product
quantity = 1
unit_price = self.delivery_amount
elif line:
product = line.test.product
quantity = line.quantity
unit_price = line.unit_price
_line = {
'type': 'line',
'unit': product.template.default_uom.id,
'product': product.id,
'quantity': quantity,
'unit_price': unit_price,
'description': product.rec_name,
}
return _line
class CreateInvoiceStart(ModelView):
'laboratory Create Invoice Start'
__name__ = 'laboratory.create_invoice.start'
start_date = fields.Date('Start Date', required=True)
end_date = fields.Date('End Date', required=True)
date_invoice = fields.Date('Date of Invoice', help='if has not date, this date will be today')
parties = fields.Many2Many('party.party', None, None, 'Parties', required=True)
company = fields.Many2One('company.company', 'Company', required=True)
@staticmethod
def default_company():
return Transaction().context.get('company')
class CreateInvoice(Wizard):
'laboratory Create Invoice'
__name__ = 'laboratory.create_invoice'
start = StateView('laboratory.create_invoice.start',
'laboratory.create_invoice_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Generate', 'generate_', 'tryton-ok', default=True),
])
generate_ = StateTransition()
def transition_generate_(self):
pool = Pool()
Order = pool.get('laboratory.order')
Sale = pool.get('sale.sale')
Invoice = pool.get('account.invoice')
parties = [party.id for party in self.start.parties]
dom = [
('company', '=', self.start.company.id),
('customer', 'in', parties),
('order_date', '>=', self.start.start_date),
('order_date', '<=', self.start.end_date),
('state', '=', 'finished'),
('status_invoice', '!=', 'invoiced'),
]
order = [('customer', 'ASC'), ('order_date', 'ASC')]
orders = Order.search(dom, order=order)
if orders:
parties = list(set([x.customer.id for x in orders]))
sales = []
party_orders = {}
for party in parties:
orders_by_party = filter(lambda x: x.customer.id == party, orders)
sale, order_ids = self.get_sale(orders_by_party, party)
sales.append(sale)
party_orders[party] = order_ids
if sales:
for sale in sales:
Sale.quote([sale])
Sale.confirm([sale])
Sale.process([sale])
if sale.invoices:
invoice = sale.invoices[0]
orders_add = party_orders[sale.invoice_party.id]
Invoice.write([invoice], {'annexes': [
('add', orders_add)]})
if invoice.annexes:
Order.write(list(invoice.annexes), {
'invoice': invoice.id,
'status_invoice': 'invoiced'
})
return 'end'
def get_sale(self, orders, party):
pool = Pool()
Party = pool.get('party.party')
Sale = pool.get('sale.sale')
party = Party(party)
address = Party.address_get(party, type='invoice')
if not party.customer_payment_term:
raise ValidatePaymentTermParty(
gettext('laboratory.msg_missing_payment_term', s=party.id_number))
payment_term = party.customer_payment_term.id
invoice_party = party.id
_lines = {}
order_ids = []
for order in orders:
for line in order.lines:
prod_id = line.test.product.id
if prod_id not in _lines.keys():
_line = order.get_line_order(line=line)
_lines.update({prod_id: _line})
else:
_lines[prod_id]['quantity'] += line.quantity
order_ids.append(order.id)
data = {
'company': self.start.company.id,
'payment_term': payment_term,
'invoice_party': invoice_party,
'invoice_type': 'P',
'currency': 31, #FIXME
'party': invoice_party,
'sale_date': self.start.date_invoice,
'state': 'draft',
'invoice_address': address,
'shipment_address': address,
'lines': [('create', _lines.values())]
}
sale, = Sale.create([data])
return sale, order_ids
class PrintServiceOrder(Wizard):
'Print Service Order Report'

View File

@ -148,6 +148,19 @@ this repository contains the full copyright notices and license terms. -->
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.ui.view" id="create_invoice_start_view_form">
<field name="model">laboratory.create_invoice.start</field>
<field name="type">form</field>
<field name="name">create_invoice_start_form</field>
</record>
<record model="ir.action.wizard" id="wizard_create_invoice">
<field name="name">Create Invoice</field>
<field name="wiz_name">laboratory.create_invoice</field>
</record>
<menuitem parent="laboratory.menu_laboratory" action="wizard_create_invoice"
id="menu_create_invoice" icon="tryton-list"/>
<record model="ir.ui.view" id="order_indicators_start_view_form">
<field name="model">laboratory.order_indicators.start</field>
<field name="type">form</field>
@ -182,6 +195,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="report_name">laboratory.efficacy_month_report</field>
<field name="report">laboratory/efficacy_month.ods</field>
</record>
<menuitem parent="laboratory.menu_reports" action="wizard_efficacy_month"
id="menu_efficacy_month" icon="tryton-print"/>

View File

@ -12,3 +12,4 @@ xml:
test_type.xml
service_order.xml
sale.xml
invoice.xml

View File

@ -2,8 +2,14 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<label name="laboratory_service_order_sequence"/>
<field name="laboratory_service_order_sequence"/>
<separator id="general" string="General" colspan="4"/>
<label name="company"/>
<field name="company"/>
<separator id="service_order" string="Service Order" colspan="4"/>
<label name="laboratory_service_order_sequence"/>
<field name="laboratory_service_order_sequence"/>
<label name="co_pay_product"/>
<field name="co_pay_product"/>
<label name="delivery_product"/>
<field name="delivery_product"/>
</form>

View File

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<label name="company"/>
<field name="company"/>
<label name="date_invoice"/>
<field name="date_invoice"/>
<label name="start_date"/>
<field name="start_date"/>
<label name="end_date"/>
<field name="end_date"/>
<field name="parties" colspan="4"/>
</form>

14
view/invoice_form.xml Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath
expr="/form/notebook/page[@id='info']"
position="after">
<page string="Annexes" id="annexes" col="4">
<field name="annexes" colspan="4"/>
</page>
</xpath>
</data>

View File

@ -16,10 +16,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="reference"/>
<label name="payment_term"/>
<field name="payment_term"/>
<label name="received_by"/>
<field name="received_by"/>
<label name="patient_payment"/>
<field name="patient_payment"/>
<newline/>
<label name="description"/>
<field name="description" colspan="3"/>
<notebook colspan="6">
@ -29,14 +26,30 @@ this repository contains the full copyright notices and license terms. -->
<page string="Other Information" id="info" col="4">
<label name="company"/>
<field name="company"/>
<label name="co_pay_invoice"/>
<field name="co_pay_invoice"/>
<label name="invoice"/>
<field name="invoice"/>
<label name="received_by"/>
<field name="received_by"/>
<separator name="notes" colspan="4"/>
<field name="notes" colspan="4"/>
</page>
<page string="Adittional Cost" id="info" col="4">
<label name="co_pay"/>
<field name="co_pay"/>
<label name="delivery_amount"/>
<field name="delivery_amount"/>
<label name="discount_amount"/>
<field name="discount_amount"/>
</page>
</notebook>
<group col="7" colspan="4" id="state_buttons">
<label name="state"/>
<field name="state"/>
<group col="4" colspan="2" id="buttons">
<label name="status_invoice"/>
<field name="status_invoice"/>
<group col="5" colspan="2" id="buttons">
<button name="draft" string="Draft"
icon="tryton-clear"/>
<button name="reception" string="Reception"

View File

@ -6,6 +6,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="number"/>
<field name="patient"/>
<field name="order_date"/>
<field name="co_pay_invoice"/>
<field name="invoice"/>
<field name="reference"/>
<field name="received_by"/>
<field name="state"/>

View File

@ -3,7 +3,7 @@
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/form/field[@name='shipment_address']" position="after">
<label name="patient_payment"/>
<field name="patient_payment"/>
<!-- <label name="patient_payment"/>
<field name="patient_payment"/> -->
</xpath>
</data>