fix and tests

This commit is contained in:
?ngel ?lvarez 2013-12-30 15:57:45 +01:00
parent f2b8ee4488
commit e9ac5b4b50
10 changed files with 391 additions and 61 deletions

View File

@ -1,10 +1,13 @@
#The COPYRIGHT file at the top level of this repository contains the full
#copyright notices and license terms.
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from trytond.pool import Pool
from template import *
from invoice import *
def register():
Pool.register(
Template,
InvoiceLine,
module='account_invoice_information_uom', type_='model')

View File

@ -1,21 +1,24 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import datetime
from trytond.model import ModelView, ModelSQL, fields
from trytond.model import fields
from trytond.pyson import Eval, If
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
from trytond import backend
from trytond.pool import PoolMeta
from decimal import Decimal
__all__ = ['InvoiceLine']
__metaclass__ = PoolMeta
_ZERO = Decimal('0.0')
_ROUND = Decimal('.0001')
class InvoiceLine:
__name__ = 'account.invoce.line'
__name__ = 'account.invoice.line'
info_unit = fields.Function(fields.Many2One('product.uom',
'Information UOM',
on_change_with=['product']), 'get_info_unit')
on_change_with=['product']), 'on_change_with_info_unit')
info_unit_digits = fields.Function(fields.Integer(
'Information Unit Digits', on_change_with=['info_unit']),
'on_change_with_unit_digits')
@ -26,18 +29,22 @@ class InvoiceLine:
'invisible': Eval('type') != 'line',
'required': Eval('type') == 'line',
},
on_change_with=['quantity', 'product', 'info_quantity'],
on_change=['quantity', 'unit_quantity', 'info_quantity']
depends=['type', 'info_unit_digits'])
on_change_with=['quantity', 'product', 'info_quantity',
'info_unit_price', 'product'],
on_change=['quantity', 'unit_quantity', 'unit',
'info_quantity', 'info_unit_price', 'product'],
depends=['type', 'info_unit_digits', 'product', 'unit', 'unit_digits'])
info_unit_price = fields.Numeric('Information Unit Price', digits=(16, 4),
states={
'invisible': Eval('type') != 'line',
'required': Eval('type') == 'line',
},
on_change=['unit_price', 'invoice_type']
on_change_with=['unit_price', 'type', 'invoice_type', 'product'],
depends=['type', 'invoice_type'])
on_change=['info_unit_price', 'invoice_type', 'product', 'info_quantity',
'invoice_type', 'type'],
on_change_with=['unit_price', 'type', 'invoice_type',
'info_unit_price', 'info_quantity', 'product', 'unit_price'],
depends=['type', 'invoice_type', 'product'])
info_amount = fields.Function(fields.Numeric('Information Amount',
digits=(16, Eval('_parent_invoice', {}).get('currency_digits',
@ -45,15 +52,10 @@ class InvoiceLine:
states={
'invisible': ~Eval('type').in_(['line', 'subtotal']),
},
on_change_with=['type', 'quantity', 'unit_price', 'info_quantity',
'_parent_invoice.currency', 'currency'],
on_change_with=['type', 'info_unit_price',
'info_quantity', '_parent_invoice.currency', 'currency'],
depends=['type', 'currency_digits']), 'get_amount')
def get_info_unit(self):
if self.product and self.product.use_info_unit:
return self.product.info_unit.id
return None
@staticmethod
def default_info_unit_digits():
return 2
@ -64,31 +66,43 @@ class InvoiceLine:
return 2
def on_change_with_info_unit(self, name=None):
if not self.product or not self.product.use_info_unit:
return None
return self.product.info_unit.id
if (self.product and self.product.use_info_unit and
self.product.info_unit):
return self.product.info_unit.id
return None
def on_change_with_info_quantity(self, name=None):
if not self.product:
if not self.product or not self.quantity:
return
return self.product.calc_info_quantity(self.qty)
return self.product.calc_info_quantity(self.quantity)
def on_change_info_quantity(self, name=None):
if not self.product:
return {}
qty = self.product.calc_quantity(
self.info_quantity, self.unit)
return {
'qty': self.product.calc_info_quantity(self.info_qty, self.unit)
'quantity': float(qty)
}
def on_change_with_info_unit_price(self, name=None):
if not self.product:
return
return self.product.get_unit_price(self.info_unit_price)
if not self.unit_price:
return
if self.type in ('out_invoice', 'out_refund'):
return self.product.get_info_list_price(self.unit_price)
else:
return self.product.get_info_cost_price(self.unit_price)
def on_change_info_unit_price(self, name=None):
def on_change_info_unit_price(self, name=None):
if not self.product:
return {}
if not self.info_unit_price:
return {}
return {
'qty': self.product.calc_info_quantity(self.info_qty, self.unit)
'unit_price': self.product.get_unit_price(self.info_unit_price)
}
def on_change_with_info_amount(self, name=None):
return self.info_unit_price * self.info_quantity

17
invoice.xml Normal file
View File

@ -0,0 +1,17 @@
<?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_line_view_form">
<field name="model">account.invoice.line</field>
<field name="inherit" ref="account_invoice.invoice_line_view_form"/>
<field name="name">invoice_line_form</field>
</record>
<record model="ir.ui.view" id="invoice_line_view_tree">
<field name="model">account.invoice.line</field>
<field name="inherit" ref="account_invoice.invoice_line_view_tree"/>
<field name="name">invoice_line_tree</field>
</record>
</data>
</tryton>

View File

@ -3,11 +3,12 @@
import datetime
from trytond.model import ModelView, ModelSQL, fields
from trytond.pyson import Eval, If
from trytond.pyson import Eval, Bool
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
from trytond import backend
from decimal import Decimal
import sys
__all__ = ['Template']
__metaclass__ = PoolMeta
@ -15,13 +16,16 @@ __metaclass__ = PoolMeta
_ZERO = Decimal('0.0')
_ROUND = Decimal('.0001')
class Template:
__name__ = "product.template"
use_info_unit = fields.Boolean('Use Information UOM')
info_unit = fields.Many2One('product.uom', 'Information UOM')
info_unit = fields.Many2One('product.uom', 'Information UOM',
states={'required': Bool(Eval('use_info_unit'))})
info_list_price = fields.Function(fields.Numeric('Information List Price',
digits=(16, 8), on_change=['list_price', 'info_list_price',
digits=(16, 8),
on_change=['list_price', 'info_list_price',
'info_ratio', 'use_info_unit'],
on_change_with=['info_ratio', 'list_price', 'use_info_unit']),
'on_change_with_info_list_price', setter='set_info_list_price')
@ -30,36 +34,37 @@ class Template:
'info_ratio', 'use_info_unit'],
on_change_with=['info_ratio', 'cost_price', 'use_info_unit']),
'on_change_with_info_cost_price', setter='set_info_cost_price')
info_ratio = fields.Numeric('Information Ratio', digits=(16, 4))
info_ratio = fields.Numeric('Information Ratio', digits=(16, 4),
states={'required': Bool(Eval('use_info_unit'))})
def calc_info_quantity(self, qty):
if self.use_info_unit:
if not self.use_info_unit:
return _ZERO
Uom = Pool().get('product.uom')
info_qty = self.info_ratio * qty
return Uom.compute_qty([self], self.info_unit, info_qty)
info_qty = self.info_ratio * Decimal(str(qty))
print "a:", info_qty
return info_qty
def calc_quantity(self, info_qty, uom):
if self.use_info_unit:
if not self.use_info_unit:
return _ZERO
Uom = Pool().get('product.uom')
qty = info_qty / self.info_ratio
return Uom.compute_qty([self], uom, qty)
return (qty).quantize(_ROUND)
def get_info_list_price(self):
if self.use_info_uom:
if self.use_info_unit and self.info_ratio and self.list_price:
return (self.info_ratio * self.list_price).quantize(_ROUND)
return _ZERO
def get_info_cost_price(self):
if self.use_info_uom:
return (self.info_ratio * self.cost_price).quantize(_ROUND)
def get_info_cost_price(self, value=None):
if not value:
value = self.cost_price
if self.use_info_unit and self.info_ratio and self.cost_price:
return (self.info_ratio * value).quantize(_ROUND)
return _ZERO
def get_unit_price(self, info_price):
if self.use_info_uom:
if self.use_info_unit:
return (info_price / self.info_ratio).quantize(_ROUND)
return _ZERO
@ -81,13 +86,19 @@ class Template:
@classmethod
def set_info_list_price(cls, templates, name, value):
print >> sys.stderr, "hola:", templates, name, value
if not value:
return
for template in templates:
template.list_price = template.get_list_price(value)
template.list_price = template.get_unit_price(value)
template.save()
@classmethod
def set_info_cost_price(cls, templates, name, value):
print >> sys.stderr, "hola2:", templates, name, value
if not value:
return
for template in templates:
template.cost_price = template.get_cost_price(value)
template.cost_price = template.get_unit_price(value)
template.save()

View File

@ -0,0 +1,258 @@
=========================
Invoice Supplier Scenario
=========================
Imports::
>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> from decimal import Decimal
>>> from operator import attrgetter
>>> from proteus import config, Model, Wizard
>>> today = datetime.date.today()
Create database::
>>> config = config.set_trytond()
>>> config.pool.test = True
Install account_invoice::
>>> Module = Model.get('ir.module.module')
>>> account_invoice_module, = Module.find(
... [('name', '=', 'account_invoice_information_uom')])
>>> Module.install([account_invoice_module.id], config.context)
>>> Wizard('ir.module.module.install_upgrade').execute('upgrade')
Create company::
>>> Currency = Model.get('currency.currency')
>>> CurrencyRate = Model.get('currency.currency.rate')
>>> currencies = Currency.find([('code', '=', 'USD')])
>>> if not currencies:
... currency = Currency(name='US Dollar', symbol=u'$', code='USD',
... rounding=Decimal('0.01'), mon_grouping='[]',
... mon_decimal_point='.')
... currency.save()
... CurrencyRate(date=today + relativedelta(month=1, day=1),
... rate=Decimal('1.0'), currency=currency).save()
... else:
... currency, = currencies
>>> Company = Model.get('company.company')
>>> Party = Model.get('party.party')
>>> company_config = Wizard('company.company.config')
>>> company_config.execute('company')
>>> company = company_config.form
>>> party = Party(name='Dunder Mifflin')
>>> party.save()
>>> company.party = party
>>> company.currency = currency
>>> company_config.execute('add')
>>> company, = Company.find([])
Reload the context::
>>> User = Model.get('res.user')
>>> config._context = User.get_preferences(True, config.context)
Create fiscal year::
>>> FiscalYear = Model.get('account.fiscalyear')
>>> Sequence = Model.get('ir.sequence')
>>> SequenceStrict = Model.get('ir.sequence.strict')
>>> fiscalyear = FiscalYear(name=str(today.year))
>>> fiscalyear.start_date = today + relativedelta(month=1, day=1)
>>> fiscalyear.end_date = today + relativedelta(month=12, day=31)
>>> fiscalyear.company = company
>>> post_move_seq = Sequence(name=str(today.year), code='account.move',
... company=company)
>>> post_move_seq.save()
>>> fiscalyear.post_move_sequence = post_move_seq
>>> invoice_seq = SequenceStrict(name=str(today.year),
... code='account.invoice', company=company)
>>> invoice_seq.save()
>>> fiscalyear.out_invoice_sequence = invoice_seq
>>> fiscalyear.in_invoice_sequence = invoice_seq
>>> fiscalyear.out_credit_note_sequence = invoice_seq
>>> fiscalyear.in_credit_note_sequence = invoice_seq
>>> fiscalyear.save()
>>> FiscalYear.create_period([fiscalyear.id], config.context)
Create chart of accounts::
>>> AccountTemplate = Model.get('account.account.template')
>>> Account = Model.get('account.account')
>>> account_template, = AccountTemplate.find([('parent', '=', None)])
>>> create_chart = Wizard('account.create_chart')
>>> create_chart.execute('account')
>>> create_chart.form.account_template = account_template
>>> create_chart.form.company = company
>>> create_chart.execute('create_account')
>>> receivable, = Account.find([
... ('kind', '=', 'receivable'),
... ('company', '=', company.id),
... ])
>>> payable, = Account.find([
... ('kind', '=', 'payable'),
... ('company', '=', company.id),
... ])
>>> revenue, = Account.find([
... ('kind', '=', 'revenue'),
... ('company', '=', company.id),
... ])
>>> expense, = Account.find([
... ('kind', '=', 'expense'),
... ('company', '=', company.id),
... ])
>>> account_tax, = Account.find([
... ('kind', '=', 'other'),
... ('company', '=', company.id),
... ('name', '=', 'Main Tax'),
... ])
>>> create_chart.form.account_receivable = receivable
>>> create_chart.form.account_payable = payable
>>> create_chart.execute('create_properties')
Create tax::
>>> TaxCode = Model.get('account.tax.code')
>>> Tax = Model.get('account.tax')
>>> tax = Tax()
>>> tax.name = 'Tax'
>>> tax.description = 'Tax'
>>> tax.type = 'percentage'
>>> tax.rate = Decimal('.10')
>>> tax.invoice_account = account_tax
>>> tax.credit_note_account = account_tax
>>> invoice_base_code = TaxCode(name='invoice base')
>>> invoice_base_code.save()
>>> tax.invoice_base_code = invoice_base_code
>>> invoice_tax_code = TaxCode(name='invoice tax')
>>> invoice_tax_code.save()
>>> tax.invoice_tax_code = invoice_tax_code
>>> credit_note_base_code = TaxCode(name='credit note base')
>>> credit_note_base_code.save()
>>> tax.credit_note_base_code = credit_note_base_code
>>> credit_note_tax_code = TaxCode(name='credit note tax')
>>> credit_note_tax_code.save()
>>> tax.credit_note_tax_code = credit_note_tax_code
>>> tax.save()
Create party::
>>> Party = Model.get('party.party')
>>> party = Party(name='Party')
>>> party.save()
Create product::
>>> ProductUom = Model.get('product.uom')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> unit2, = ProductUom.find([('name', '=', 'Kilogram')])
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> product = Product()
>>> template = ProductTemplate()
>>> template.name = 'product'
>>> template.default_uom = unit
>>> template.use_info_unit = True
>>> template.info_unit = unit2
>>> template.info_ratio = Decimal('2')
>>> template.type = 'service'
>>> template.list_price = Decimal('40')
>>> template.cost_price = Decimal('20')
>>> template.account_expense = expense
>>> template.account_revenue = revenue
>>> template.supplier_taxes.append(tax)
>>> template.info_cost_price
Decimal('40.0000')
>>> template.info_list_price
Decimal('80.0000')
>>> template.info_cost_price
Decimal('40.0000')
>>> product.template = template
>>> product.save()
Create payment term::
>>> PaymentTerm = Model.get('account.invoice.payment_term')
>>> PaymentTermLine = Model.get('account.invoice.payment_term.line')
>>> payment_term = PaymentTerm(name='Term')
>>> payment_term_line = PaymentTermLine(type='remainder')
>>> payment_term.lines.append(payment_term_line)
>>> payment_term.save()
Create invoice::
>>> Invoice = Model.get('account.invoice')
>>> InvoiceLine = Model.get('account.invoice.line')
>>> invoice = Invoice()
>>> invoice.type = 'in_invoice'
>>> invoice.party = party
>>> invoice.payment_term = payment_term
>>> invoice.invoice_date = today
>>> line = InvoiceLine()
>>> invoice.lines.append(line)
>>> line.product = product
>>> line.quantity = 5
>>> line = InvoiceLine()
>>> invoice.lines.append(line)
>>> line.account = expense
>>> line.description = 'Test'
>>> line.quantity = 1
>>> line.unit_price = Decimal(10)
>>> invoice.untaxed_amount == Decimal(110)
True
>>> invoice.tax_amount == Decimal(10)
True
>>> invoice.total_amount == Decimal(120)
True
>>> invoice.save()
>>> Invoice.post([invoice.id], config.context)
>>> invoice.reload()
>>> invoice.state
u'posted'
>>> invoice.untaxed_amount == Decimal(110)
True
>>> invoice.tax_amount == Decimal(10)
True
>>> invoice.total_amount == Decimal(120)
True
>>> payable.reload()
>>> (payable.debit, payable.credit) == \
... (Decimal(0), Decimal(120))
True
>>> expense.reload()
>>> (expense.debit, expense.credit) == \
... (Decimal(110), Decimal(0))
True
>>> account_tax.reload()
>>> (account_tax.debit, account_tax.credit) == \
... (Decimal(10), Decimal(0))
True
>>> invoice_base_code.reload()
>>> invoice_base_code.sum == Decimal(100)
True
>>> invoice_tax_code.reload()
>>> invoice_tax_code.sum == Decimal(10)
True
>>> credit_note_base_code.reload()
>>> credit_note_base_code.sum == Decimal(0)
True
>>> credit_note_tax_code.reload()
>>> credit_note_tax_code.sum == Decimal(0)
True
Credit invoice::
>>> credit = Wizard('account.invoice.credit', [invoice])
>>> credit.form.with_refund = False
>>> credit.execute('credit')
>>> credit_note, = Invoice.find([('type', '=', 'in_credit_note')])
>>> credit_note.state
u'draft'
>>> credit_note.untaxed_amount == invoice.untaxed_amount
True
>>> credit_note.tax_amount == invoice.tax_amount
True
>>> credit_note.total_amount == invoice.total_amount
True

View File

@ -10,7 +10,7 @@ if os.path.isdir(DIR):
sys.path.insert(0, os.path.dirname(DIR))
import unittest
#import doctest TODO: Remove if no sceneario needed.
import doctest
import trytond.tests.test_tryton
from trytond.tests.test_tryton import test_view, test_depends
from trytond.backend.sqlite.database import Database as SQLiteDatabase
@ -49,11 +49,10 @@ def doctest_dropdb(test):
def suite():
suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase))
# TODO: remove if no scenario needed.
#suite.addTests(doctest.DocFileSuite('scenario_invoice.rst',
# setUp=doctest_dropdb, tearDown=doctest_dropdb, encoding='utf-8',
# optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase))
suite.addTests(doctest.DocFileSuite('account_invoice_information_uom.rst',
setUp=doctest_dropdb, tearDown=doctest_dropdb, encoding='utf-8',
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
return suite
if __name__ == '__main__':

View File

@ -4,3 +4,4 @@ depends:
account_invoice
xml:
template.xml
invoice.xml

View File

@ -0,0 +1,15 @@
<?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='general']/field[@name='taxes']" position="before">
<label name="info_unit"/>
<field name="info_unit"/>
<label name="info_quantity"/>
<field name="info_quantity"/>
<label name="info_unit_price"/>
<field name="info_unit_price"/>
<label name="info_amount"/>
<field name="info_amount"/>
</xpath>
</data>

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. -->
<data>
<xpath expr="/tree/field[@name='amount']" position="after">
<field name="info_unit"/>
<field name="info_quantity"/>
<field name="info_unit_price"/>
<field name="info_amount"/>
</xpath>
</data>

View File

@ -4,12 +4,13 @@ this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/form/notebook/page[@id='general']" position="after">
<page id="information_uom" string="Information UOM">
<label name="use_info_uom"/>
<field name="use_info_uom"/>
<label name="use_info_unit"/>
<field name="use_info_unit"/>
<newline/>
<label name="info_ratio"/>
<field name="info_ratio"/>
<label name="info_uom"/>
<field name="info_uom"/>
<label name="info_unit"/>
<field name="info_unit"/>
<label name="info_list_price"/>
<field name="info_list_price"/>
<label name="info_cost_price"/>