trytonpsk-sale_contract/contract.py

1077 lines
40 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# This file is part of the sale_weight module for Tryton.
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from decimal import Decimal
from datetime import timedelta, date
from dateutil.relativedelta import relativedelta
from trytond.i18n import gettext
from trytond.model import Workflow, ModelView, ModelSQL, fields
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.wizard import (Wizard, StateView, Button,
StateReport, StateTransition)
from trytond.report import Report
from trytond.modules.account_col import invoice
from trytond.exceptions import UserError
import calendar
conversor = None
try:
from numword import numword_es
conversor = numword_es.NumWordES()
except Exception:
print("Warning: Does not possible import numword module!")
print("Please install it...!")
STATES = {
'readonly': Eval('state') != 'draft',
}
STATES_LINE = {
'readonly': Eval('state') != 'pending',
}
_ZERO = Decimal('0.0')
RANGE_MONTH = [(str(d + 1), str(d + 1)) for d in range(28)] + [('', '')]
TYPE_INVOICE = invoice.TYPE_INVOICE_OUT
OPERATION_TYPE_OUT = [
('', ''),
('09', 'Servicios AIU'),
('10', 'Estandar'),
('11', 'Mandatos bienes'),
('12', 'Transporte'),
('13', 'Cambiario'),
('20', 'Nota Crédito que referencia una factura electrónica'),
('22', 'Nota Crédito sin referencia a facturas'),
('30', 'Nota Débito que referencia una factura electrónica'),
('32', 'Nota Débito sin referencia a facturas'),
('SSCUFE', 'SSCUFE'),
('SSCUDE', 'SSCUDE'),
('SSPOS', 'SSPOS'),
('SSSNum', 'SSSNum'),
('SSRecaudo', 'SSRecaudo'),
('SSReporte', 'SSReporte'),
('SSSinAporte', 'SSSinAporte'),
]
class TypeContract(ModelSQL, ModelView):
"Type Sale Contract"
__name__ = 'sale.contract.type'
name = fields.Char('Name')
active = fields.Boolean('Active')
kind = fields.Selection([
('open', 'Open'),
('close', 'Close'),
], 'Kind', select=True)
invoice_frecuency = fields.Selection([
('biweekly', 'Biweekly'),
('monthly', 'Monthly'),
('annual', 'Annual'),
('just_one', 'Just One'),
], 'Invoice Frecuency', select=True, required=True)
description_template = fields.Char('Description Template')
direct_invoice = fields.Boolean('Direct Invoice')
class SaleContract(Workflow, ModelSQL, ModelView):
"Sale Contract"
__name__ = 'sale.contract'
_rec_name = 'number'
number = fields.Char('Number', readonly=True)
party = fields.Many2One('party.party', 'Party', required=True,
select=True, states=STATES)
contract_date = fields.Date('Contract Date', states={
'readonly': Eval('state') != 'draft',
'required': True,
})
# date = fields.Date('Date')
payment_term = fields.Many2One('account.invoice.payment_term',
'Payment Term', states=STATES)
lines = fields.One2Many('sale.contract.line', 'contract', 'Lines',
states={
'readonly': Eval('state') == 'finished',
# 'required': Eval('state') == 'finished',
}, depends=['state'])
company = fields.Many2One('company.company', 'Company', required=True,
states=STATES)
type = fields.Many2One('sale.contract.type', 'Type', states=STATES,
required=False)
grouping_invoice = fields.Boolean('Grouping Invoice')
state = fields.Selection([
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('finished', 'Finished'),
('canceled', 'Canceled'),
], 'State', readonly=True, select=True)
state_string = state.translated('state')
total_amount = fields.Numeric('Total Amount', digits=(16, 2),
states={'readonly': True}, depends=['invoices_number'])
validity_contract = fields.Char('Validity Contract', states=STATES)
monthly_cutoff_date = fields.Selection(RANGE_MONTH, 'Monthly Cut Off Date',
states=STATES)
start_date = fields.Date('Start Date', states=STATES)
end_date = fields.Date('End Date', states={
'readonly': Eval('state') != 'draft',
})
description = fields.Char('Description', states=STATES, required=True)
invoices_number = fields.Integer('Invoices Number',
states={'readonly': Eval('state') != 'draft'})
product_lines = fields.One2Many('sale.contract.product_line', 'contract',
'Product Lines', states={
'required': Eval('state') == 'confirmed',
'readonly': Eval('state') == 'finished',
})
fee_amount = fields.Function(fields.Numeric('Fee Amount'),
'get_fee_amount')
total_amount_words = fields.Function(fields.Char('Total Amount Words'),
'get_total_amount_words')
comment = fields.Text('Comment', states=STATES)
salesman = fields.Many2One('company.employee', 'Salesman', states=STATES)
# origins = fields.Function(fields.Reference('sale.sale', None, None,
# 'Resources'), 'get_origins')
invoice_method = fields.Selection([
('by_balance', 'By Balance'),
('only_proforma', 'Only Proforma'),
('standard', 'Standard'),
], 'Invoice Method', required=True, select=True)
reference = fields.Char('Reference')
origin = fields.Reference('Origin', selection='get_origin', select=True,
states={'readonly': Eval('state') != 'draft'},
depends=['state'])
currency = fields.Many2One('currency.currency', 'Currency')
operation_type = fields.Selection(OPERATION_TYPE_OUT, 'Operation Type')
# payment_time = fields.Selection([
# ('monthly', 'Monthly'),
# ('bimonthly', 'Bimonthly'),
# ('biannual', 'Biannual'),
# ('annual', 'annual'),
# ], 'Payment Time', select=True)
@classmethod
def __setup__(cls):
super(SaleContract, cls).__setup__()
cls._order.insert(0, ('contract_date', 'DESC'))
cls._transitions |= set((
('draft', 'confirmed'),
('draft', 'canceled'),
('confirmed', 'draft'),
('canceled', 'draft'),
('confirmed', 'finished'),
('finished', 'confirmed'),
))
cls._buttons.update({
'create_lines': {
'invisible': Eval('state') != 'confirmed',
},
'cancel': {
'invisible': Eval('state') != 'draft',
},
'draft': {
'invisible': ~Eval('state').in_(['canceled', 'confirmed']),
},
'confirm': {
'invisible': Eval('state').in_(['canceled', 'confirmed']),
},
'finish': {
'invisible': Eval('state') != 'confirmed',
},
})
@staticmethod
def default_state():
return 'draft'
@staticmethod
def default_invoice_method():
return 'standard'
@staticmethod
def default_contract_date():
Date = Pool().get('ir.date')
return Date.today()
@staticmethod
def default_company():
return Transaction().context.get('company') or False
@staticmethod
def default_currency():
Company = Pool().get('company.company')
company = Transaction().context.get('company')
if company:
return Company(company).currency.id
@classmethod
def get_origin(cls):
Model = Pool().get('ir.model')
get_name = Model.get_name
models = cls._get_origin()
return [(None, '')] + [(m, get_name(m)) for m in models]
@classmethod
def _get_origin(cls):
'Return list of Model names for origin Reference'
return ['crm.opportunity']
@classmethod
def delete(cls, contracts):
# Cancel before delete
cls.cancel(contracts)
for contract in contracts:
lines = [line for line in contract.lines if l.state in ('processed', 'paid')]
if lines:
raise UserError(gettext('sale_contract.msg_lines_processed'),
contract=contract.rec_name)
if contract.state != 'canceled':
raise UserError(gettext('sale_contract.delete_cancel'),
contract=contract.rec_name)
super(SaleContract, cls).delete(contracts)
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, contracts):
pass
@classmethod
@ModelView.button
@Workflow.transition('finished')
def finish(cls, contracts):
for contract in contracts:
contract.check_sales()
@classmethod
@ModelView.button
@Workflow.transition('canceled')
def cancel(cls, contracts):
pass
@classmethod
@ModelView.button
@Workflow.transition('confirmed')
def confirm(cls, contracts):
for contract in contracts:
contract.set_number()
@classmethod
@ModelView.button
def create_lines(cls, contracts):
for contract in contracts:
if contract.type.kind == 'close':
contract.forecast_sales()
def check_sales(self):
for line in self.lines:
if line.state == 'pending':
raise UserError(gettext('sale_contract.sales_pending'))
def forecast_sales(self):
Line = Pool().get('sale.contract.line')
lines_to_create = []
for i in range(self.invoices_number):
date_ = self.start_date + timedelta(30.5 * i)
lines_to_create.append({
'contract': self.id,
'date': date_,
'sale_amount': self.fee_amount,
'description': self.description,
'state': 'pending',
})
Line.create(lines_to_create)
def set_number(self):
'''
Set sequence number
'''
config = Pool().get('sale.configuration')(1)
if self.number:
return
if not config.sale_contract_sequence:
raise UserError(gettext('sale_contract.msg_sequence_missing'))
number = config.sale_contract_sequence.get()
self.write([self], {'number': number})
def get_fee_amount(self, name=None):
return sum([
line.unit_price for line in self.product_lines if line.type == 'line' and line.unit_price
])
def get_total_amount_words(self, name=None):
if self.total_amount and conversor:
num = (conversor.cardinal(int(self.total_amount))).upper()
return num
# def get_sales(self, name):
# sales_ids = []
# for line in self.lines:
# if line.origin:
# sales_ids.append(line.origin.id)
# return sales_ids
@fields.depends('product_lines', 'invoices_number')
def on_change_with_total_amount(self):
res = _ZERO
val_amount = _ZERO
if self.invoices_number and self.product_lines:
for line in self.product_lines:
if not line.unit_price:
continue
val_amount += line.unit_price
res = val_amount * self.invoices_number
return res
def validate_contract(self):
return True
class SaleContractLine(Workflow, ModelSQL, ModelView):
"Sale Contract Line"
__name__ = 'sale.contract.line'
_rec_name = 'description'
contract = fields.Many2One('sale.contract', 'Contract', select=True,
ondelete='CASCADE', states=STATES_LINE)
date = fields.Date('Date', required=True, states=STATES_LINE)
amount = fields.Numeric('Amount', digits=(16, 2), required=True,
states=STATES_LINE)
state = fields.Selection([
('pending', 'Pending'),
('processed', 'Processed'),
('paid', 'Paid'),
('canceled', 'Canceled'),
], 'State', readonly=True, select=True)
origin = fields.Reference('Origin', selection='get_origin', select=True,
states=STATES_LINE)
description = fields.Text('Description', states=STATES_LINE)
only_first_invoice = fields.Boolean('Only First Invoice',
states=STATES_LINE)
@classmethod
def __setup__(cls):
super(SaleContractLine, cls).__setup__()
cls._transitions |= set((
('pending', 'processed'),
('processed', 'canceled'),
('processed', 'paid'),
))
cls._buttons.update({
'cancel': {
'invisible': Eval('state') != 'paid'
},
'process': {
'invisible': Eval('state') != 'pending',
},
})
@classmethod
def __register__(cls, module_name):
super(SaleContractLine, cls).__register__(module_name)
table_h = cls.__table_handler__(module_name)
cursor = Transaction().connection.cursor()
if table_h.column_exist('sale'):
table_h.column_rename('sale_amount', 'amount')
query = """update sale_contract_line set
origin=concat('sale.sale,',cast(sale as varchar))
where sale is not null;"""
cursor.execute(query)
table_h.drop_column('sale')
@staticmethod
def default_state():
return 'pending'
@staticmethod
def default_amount():
return 0
@classmethod
def _get_origin(cls):
'Return list of Model names for origin Reference'
return [
cls.__name__,
'sale.sale',
'account.invoice',
'account.invoice.line'
]
@classmethod
def get_origin(cls):
Model = Pool().get('ir.model')
get_name = Model.get_name
models = cls._get_origin()
return [(None, '')] + [(m, get_name(m)) for m in models]
@classmethod
@ModelView.button
@Workflow.transition('processed')
def process(cls, lines, product_lines=None, date_start=None, date_end=None):
for line in lines:
contract = line.contract
if contract.type.direct_invoice:
line._create_invoice(contract, product_lines, date_start, date_end)
else:
line._create_sale(contract)
@classmethod
@ModelView.button
@Workflow.transition('canceled')
def cancel(cls, lines):
pass
def _create_sale(self, contract):
pool = Pool()
Party = pool.get('party.party')
Sale = pool.get('sale.sale')
description = contract.description
if contract.type.description_template and contract.monthly_cutoff_date:
try:
start_date = date(
self.date.year,
self.date.month,
int(contract.monthly_cutoff_date))
end_date = start_date + relativedelta(months=+1)
description = contract.type.description_template % (start_date, end_date)
except Exception:
pass
party = contract.party
sale_to_create = {
'company': contract.company.id,
'party': contract.party.id,
'sale_date': self.date,
'state': 'draft',
'salesman': contract.salesman.id if contract.salesman else None,
'invoice_address': Party.address_get(party, type='invoice'),
'shipment_address': Party.address_get(party, type='delivery'),
'description': description,
'invoice_method': 'order',
'shipment_method': 'order',
}
if hasattr(Sale, 'shop'):
shop_id = Transaction().context.get('shop')
sale_to_create['shop'] = shop_id if shop_id else None
if contract.payment_term:
sale_to_create['payment_term'] = contract.payment_term.id
lines_to_create = []
for line in contract.product_lines:
value = self.get_line(contract, line)
lines_to_create.append(value)
sale_to_create['lines'] = [('create', lines_to_create)]
sale, = Sale.create([sale_to_create])
self.write([self], {'origin': str(sale)})
sale.write([sale], {'contract_line': self.id})
return sale
def get_line(self, contract, line, date_start=None, date_end=None):
prorate_price = None
if line.prorate:
days_prorate = line.days_collect(date_start, date_end)
if days_prorate < 30:
prorate_price = line.prorating(days_prorate)
if line.type == 'subtotal':
value = {
'type': line.type,
'description': line.description,
}
else:
value = {
'type': line.type,
'product': line.product.id,
'quantity': 1,
'unit': line.product.default_uom.id,
'unit_price': line.unit_price if not prorate_price else prorate_price,
'taxes': [],
'description': line.description,
}
taxes_ids = self.get_taxes(contract.party, line)
value['taxes'] = [('add', taxes_ids)]
if hasattr(line, 'operation_center'):
value['operation_center'] = line.operation_center
return value
def get_taxes(self, party, line):
taxes = []
pattern = {}
for tax in line.product.customer_taxes_used:
if party.customer_tax_rule:
tax_ids = party.customer_tax_rule.apply(tax, pattern)
if tax_ids:
taxes.extend(tax_ids)
continue
taxes.append(tax.id)
if party.customer_tax_rule:
for line in party.customer_tax_rule.lines:
tax_ids = party.customer_tax_rule.apply(line.tax, pattern)
if tax_ids:
taxes.extend(tax_ids)
taxes.extend(line.taxes)
return taxes
def _create_invoice(self, contract, product_lines, start_date_period=None, end_date_period=None):
pool = Pool()
Party = pool.get('party.party')
Invoice = pool.get('account.invoice')
InvoiceLine = pool.get('account.invoice.line')
description = contract.description
if contract.type.description_template and contract.monthly_cutoff_date:
try:
start_date = date(
self.date.year,
self.date.month,
int(contract.monthly_cutoff_date))
end_date = start_date + relativedelta(months=+1)
description = contract.type.description_template % (start_date, end_date)
except Exception:
pass
party = contract.party
dpm = str(start_date_period.month) if start_date_period.month >= 10 else '0' + str(start_date_period.month)
invoice = Invoice(
company=contract.company.id,
party=party.id,
invoice_date=self.date,
state='draft',
operation_type=contract.operation_type,
invoice_type='1',
salesman=contract.salesman.id if contract.salesman else None,
invoice_address=Party.address_get(party, type='invoice'),
description=description,
account=party.account_receivable.id,
period=str(start_date_period.year) + '-' + dpm,
type='out',
)
if hasattr(Invoice, 'shop'):
shop_id = Transaction().context.get('shop')
invoice.shop = shop_id if shop_id else None
if contract.payment_term:
invoice.payment_term = contract.payment_term.id
lines_to_create = []
invoice.on_change_type()
invoice.on_change_invoice_type()
invoice.operation_type = contract.operation_type
invoice.save()
lines_without_aiu = [ln for ln in product_lines if not ln.relation_aiu]
for line in lines_without_aiu:
value = self.get_line(contract, line, start_date_period, end_date_period)
value['invoice'] = invoice
value['account'] = line.product.account_revenue_used
value['note'] = line.note
value['invoice_type'] = 'out'
products_aiu = []
for ln in line.products_aiu:
value_contract = self.get_line(contract, ln, start_date_period, end_date_period)
value_contract['invoice'] = invoice
value_contract['invoice_type'] = 'out'
value_contract['note'] = ln.note
value_contract['account'] = ln.product.account_revenue_used
products_aiu.append(value_contract)
if products_aiu:
value['products_aiu'] = [('create', products_aiu)]
lines_to_create.append(value)
InvoiceLine.create(lines_to_create)
invoice.on_change_taxes()
invoice.save()
self.write([self], {'origin': str(invoice)})
return invoice
def validate_contract_line(self):
pass
def _process_sale(self, invoice_type=None, period_name=None):
Sale = Pool().get('sale.sale')
if not self.origin or self.origin.__name__ != 'sale.sale':
return
sale = self.origin
if invoice_type:
Sale.write([sale], {'invoice_type': invoice_type})
if period_name:
Sale.write([sale], {'reference': period_name})
Sale.quote([sale])
# if self.contract.invoice_method:
# self.sale.invoice_method = self.contract.invoice_method
Sale.confirm([sale])
Sale.process([sale])
for inv in sale.invoices:
inv.invoice_date = self.date
inv.reference = sale.reference or ''
inv.on_change_invoice_type()
inv.save()
class ContractProductLine(ModelSQL, ModelView):
"Contract Product Line"
__name__ = 'sale.contract.product_line'
contract = fields.Many2One('sale.contract', 'Contract', select=True,
ondelete='CASCADE')
product = fields.Many2One('product.product', 'Product',
states={
'invisible': Eval('type') != 'line',
'required': Eval('type') == 'line',
},depends=['type'])
prorate = fields.Boolean('Prorate')
unit_price = fields.Numeric('Unit Price', digits=(16, 2),
states={
'invisible': Eval('type') != 'line',
'required': Eval('type') == 'line',
},
depends=['type'])
description = fields.Text('Description', required=True)
only_first_invoice = fields.Boolean('Only First Invoice')
type = fields.Selection([
('line', 'Line'),
('subtotal', 'Subtotal'),
], 'Type', select=True, required=True)
taxes = fields.Many2Many(
'sale.contract-account.tax', 'line', 'tax', 'Taxes',
order=[
('tax.sequence', 'ASC'),
('tax.id', 'ASC')],
domain=[
('parent', '=', None),
[
'OR',
('group', '=', None),
('group.kind', 'in', ['sale', 'both'])],
('company', '=', Eval('_parent_contract', {}).get('company', -1)),
],
states={
'invisible': Eval('type') != 'line',
'readonly': ~Eval('contract_state').in_(['draft', None]),
}, depends=['type', 'contract_state'])
contract_state = fields.Function(
fields.Selection('get_contract_states', "Contract State"),
'on_change_with_contract_state')
relation_aiu = fields.Many2One('sale.contract.product_line', 'Relation AIU',
domain=[
('contract', '=', Eval('contract')),
('id', '!=', Eval('id'))
],
states={'invisible': Eval('_parent_contract', {}).get('operation_type', '') != '09'},
depends=['contract'])
products_aiu = fields.One2Many('sale.contract.product_line', 'relation_aiu', 'Products AIU',
states={
'invisible': ~Eval('products_aiu'),
'readonly': True,
}
)
note = fields.Text('Note')
start_date = fields.Date('Start Date')
end_date = fields.Date('End Date')
@fields.depends('products_aiu', '_parent_contract.operation_type', 'product')
def on_change_products_aiu(self):
if self.contract.operation_type == '09' and self.products_aiu and self.product:
self.note = 'Contrato de servicios AIU por concepto de: ' + self.product.description
else:
self.note = None
@fields.depends('contract', '_parent_contract.state')
def on_change_with_contract_state(self, name=None):
if self.contract:
return self.contract.state
@classmethod
def get_contract_states(cls):
pool = Pool()
Contract = pool.get('sale.contract')
return Contract.fields_get(['state'])['state']['selection']
@fields.depends('product')
def on_change_with_unit_price(self):
if self.product:
return self.product.list_price
def get_rec_name(self, name):
return self.product.rec_name
@staticmethod
def default_type():
return 'line'
def prorating(self, days):
base_price = self.unit_price/30
return round(base_price * days, 2)
# def days_collect(self, date):
# if self.start_date and self.end_date:
# if self.end_date.month == date.month:
# date_diference = self.end_date - self.start_date
# date_diference_days = date_diference.days
# if date_diference_days <= 30:
# return date_diference_days
# else:
# date_diference = self.end_date - date
# return date_diference.days
# if self.start_date > date:
# date_diference = self.start_date - date
# return date_diference.days
# return 30
def days_collect(self, period_start, period_end):
date_diference = 30
if self.start_date <= period_start:
if self.end_date >= period_end:
return date_diference
if self.end_date <= period_end:
total_days_month = calendar.monthrange(period_start.year, period_start.month)[1]
if total_days_month == 29 and self.end_date.day == 28:
date_diference = 30
else:
date_diference = (self.end_date - period_start).days + 1
elif self.start_date > period_start and self.end_date >= period_end:
date_diference = (period_end - self.start_date).days
elif self.start_date > period_start and self.end_date < period_end:
date_diference = (self.end_date - self.start_date).days
return date_diference
class ContractProductTax(ModelSQL):
"Contract Product Tax"
__name__ = 'sale.contract-account.tax'
_table = 'sale_contract_account_tax'
line = fields.Many2One('sale.contract.product_line', 'Contract Product Tax', ondelete='CASCADE',
select=True, required=True)
tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
select=True, required=True)
class SaleContractReport(Report):
__name__ = 'sale_contract.contract_report'
@classmethod
def get_context(cls, records, header, data):
report_context = super(SaleContractReport, cls).get_context(records, header, data)
Company = Pool().get('company.company')
company_id = Transaction().context.get('company')
report_context['company'] = Company(company_id)
return report_context
class SaleContractByMonthStart(ModelView):
'Sale Contract By Month Start'
__name__ = 'sale.contract_by_month.start'
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year', required=True)
company = fields.Many2One('company.company', 'Company', required=True)
period = fields.Many2One('account.period', 'Period', required=True,
depends=['fiscalyear'], domain=[
('fiscalyear', '=', Eval('fiscalyear')),
])
@staticmethod
def default_company():
return Transaction().context.get('company')
@fields.depends('fiscalyear')
def on_change_fiscalyear(self):
self.period = None
@staticmethod
def default_fiscalyear():
FiscalYear = Pool().get('account.fiscalyear')
return FiscalYear.find(
Transaction().context.get('company'), exception=False)
class SaleContractByMonth(Wizard):
'Sale Contract By Month'
__name__ = 'sale.contract_by_month'
start = StateView('sale.contract_by_month.start',
'sale_contract.sale_contract_by_month_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('sale_contract.contract_by_month_report')
def do_print_(self, action):
data = {
'company': self.start.company.id,
'fiscalyear': self.start.fiscalyear.id,
'period': self.start.period.id,
}
return action, data
def transition_print_(self):
return 'end'
class SaleContractByMonthReport(Report):
'Contract By Month Report'
__name__ = 'sale_contract.contract_by_month_report'
@classmethod
def get_context(cls, records, header, data):
report_context = super(SaleContractByMonthReport, cls).get_context(
records, header, data
)
pool = Pool()
SaleContract = pool.get('sale.contract')
Period = pool.get('account.period')
period = Period(data['period'])
records = SaleContract.search([
('company', '=', data['company']),
('contract_date', '>=', period.start_date),
('contract_date', '<=', period.end_date),
('state', 'in', ['confirmed', 'finished']),
])
total_amount = []
for contract in records:
total_amount.append(contract.total_amount)
report_context['sum_total_amount'] = sum(total_amount)
report_context['period'] = period.name
report_context['today'] = date.today()
report_context['records'] = records
return report_context
class CreateInvoicesFromContractStart(ModelView):
'Create Invoices From Contract Start'
__name__ = 'sale.create_invoices_from_contract.start'
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year', required=True)
company = fields.Many2One('company.company', 'Company', required=True)
party = fields.Many2One('party.party', 'Party')
period = fields.Many2One('account.period', 'Period',
depends=['fiscalyear'], required=True, domain=[
('fiscalyear', '=', Eval('fiscalyear')),
])
invoice_type = fields.Selection(TYPE_INVOICE, 'Type Invoice')
contracts = fields.Many2Many('sale.contract', None, None, 'Contracts',
domain=[('party', '=', Eval('party')), ('state', '=', 'confirmed')], depends=['party'])
@staticmethod
def default_company():
return Transaction().context.get('company')
@fields.depends('fiscalyear')
def on_change_fiscalyear(self):
self.period = None
@staticmethod
def default_fiscalyear():
FiscalYear = Pool().get('account.fiscalyear')
return FiscalYear.find(
Transaction().context.get('company'), exception=False)
class CreateInvoicesFromContract(Wizard):
'Create Invoices From Contract'
__name__ = 'sale.create_invoices_from_contract'
start = StateView('sale.create_invoices_from_contract.start',
'sale_contract.create_invoices_from_contract_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Create', 'accept', 'tryton-ok', default=True),
])
accept = StateTransition()
def transition_accept(self):
pool = Pool()
Contract = pool.get('sale.contract')
Line = Pool().get('sale.contract.line')
lines = Line.search([
('contract.state', '=', 'confirmed'),
('date', '>=', self.start.period.start_date),
('date', '<=', self.start.period.end_date),
])
lines_invoiced = []
if lines:
lines_invoiced = [line.contract.id for line in lines]
dom_contract = [
['AND', [
'OR', [
('type.kind', '=', 'open'),
('state', '=', 'confirmed'),
('end_date', '>=', self.start.period.start_date),
], [
('type.kind', '=', 'open'),
('state', '=', 'confirmed'),
('end_date', '=', None),
],
]],
]
if lines_invoiced:
dom_contract = [
['AND', [
'OR', [
('type.kind', '=', 'open'),
('state', '=', 'confirmed'),
('end_date', '>=', self.start.period.start_date),
('id', 'not in', lines_invoiced),
], [
('type.kind', '=', 'open'),
('state', '=', 'confirmed'),
('end_date', '=', None),
('id', 'not in', lines_invoiced),
],
]],
]
if self.start.party:
dom_contract.append(('party', '=', self.start.party))
if self.start.contracts:
contract_ids = [contract.id for contract in self.start.contracts]
dom_contract.append(('id', 'in', contract_ids))
contracts = Contract.search(dom_contract)
lines_to_create = []
period_start_date = self.start.period.start_date
period_end_date = self.start.period.end_date
for contract in contracts:
validate_dates = None
lines_to_products = []
original_lines = []
for product in contract.product_lines:
validate_dates = self.validate_dates(product, self.start.period)
if validate_dates:
validate = contract.validate_contract()
if validate:
day_ = period_start_date.day
prorate_price = None
if contract.start_date > period_start_date:
day_ = contract.start_date.day
if product.prorate:
days_prorate = product.days_collect(period_start_date, period_end_date)
if days_prorate < 30:
prorate_price = product.prorating(days_prorate)
lines_to_create.append({
'contract': contract.id,
'date': date(period_start_date.year, period_start_date.month, day_),
'amount': product.unit_price if not prorate_price else prorate_price,
'description': product.description,
'state': 'pending',
})
lines_to_products.append(product)
lines = Line.create(lines_to_create)
lines_to_create = []
original_lines.extend(lines)
if not contract.grouping_invoice:
for contract_line in lines:
if not product.relation_aiu:
Line.process([contract_line], [product], period_start_date)
if not contract_line.origin or (contract_line.origin and contract_line.origin.__name__ == 'account.invoice'):
continue
contract_line.validate_contract_line()
contract_line._process_sale(
self.start.invoice_type, self.start.period.name
)
if len(lines_to_products) > 0:
invoice_tmp = original_lines[0]._create_invoice(contract, lines_to_products, period_start_date, period_end_date)
for contract_line in original_lines:
if not contract_line.origin:
contract_line.origin = str(invoice_tmp)
contract_line.save()
return 'end'
def validate_dates(self, product, period):
if product.start_date and product.end_date:
if product.start_date >= period.start_date and product.end_date <= period.end_date:
return True
if product.start_date > period.end_date:
return False
elif product.start_date <= period.end_date and not product.end_date:
return True
elif product.end_date >= period.start_date and product.end_date <= period.end_date:
return True
elif product.end_date >= period.end_date:
return True
else:
False
if product.start_date and not product.end_date:
if product.start_date < period.end_date:
return True
else:
return False
def same_month(self, date1, date2):
return date1.year == date2.year and date1.month == date2.month
class SaleContractFromPartyStart(ModelView):
'Create Sale Contract From Parties Start'
__name__ = 'sale.create_contract_from_party.start'
start_date = fields.Date('Start Date', required=True)
end_date = fields.Date('End Date',)
payment_term = fields.Many2One('account.invoice.payment_term',
'Payment Term', required=True)
parties = fields.One2Many('party.party', None, 'Parties', add_remove=[],
required=True)
products = fields.One2Many('product.product', None, 'Products',
add_remove=[], required=True)
# salesman = fields.Many2One('company.employee', 'Salesman')
company = fields.Many2One('company.company', 'Company', required=True)
type_contract = fields.Many2One('sale.contract.type', 'Type Contract',
required=True)
cuttof_day = fields.Numeric('cuttof_day')
@staticmethod
def default_company():
return Transaction().context.get('company')
@fields.depends('start_date', 'end_date')
def on_change_start_date(self):
if self.start_date:
self.end_date = self.start_date + timedelta(days=365)
class SaleContractFromParty(Wizard):
'Create Contract From Parties'
__name__ = 'sale.create_contract_from_party'
start = StateView('sale.create_contract_from_party.start',
'sale_contract.create_contract_from_party_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Create', 'accept', 'tryton-ok', default=True),
])
accept = StateTransition()
def transition_accept(self):
pool = Pool()
Contract = pool.get('sale.contract')
Date = pool.get('ir.date')
for party in self.start.parties:
contract_to_create = {
'company': self.start.company.id,
'party': party.id,
'contract_date': self.start.start_date or Date.today(),
'payment_term': self.start.payment_term.id or None,
'type': self.start.type_contract.id,
'start_date': self.start.start_date,
'end_date': self.start.end_date or None,
'comment': '',
'total_amount': 1,
'description': ' ',
'invoices_number': 1,
'invoice_method': 'order',
'state': 'draft',
'product_lines': [],
'monthly_cutoff_date': self.start.cuttof_day or None,
}
lines_to_create = []
for product in self.start.products:
value = {
# 'analytic_account': None,
'description': product.description or product.name,
'product': product.id,
'type': 'line',
'unit_price': product.list_price,
'only_first_invoice': False,
}
lines_to_create.append(value)
contract_to_create['product_lines'] = [('create', lines_to_create)]
Contract.create([contract_to_create])
return 'end'