Matteo has proposed merging lp:~openobject-italia-core-devs/openobject-italia/l10n_it_patches into lp:openobject-italia.
Requested reviews: OpenERP Italia core devs (openobject-italia-core-devs) For more details, see: https://code.launchpad.net/~openobject-italia-core-devs/openobject-italia/l10n_it_patches/+merge/60050 Sviluppato il modulo l10n_it_patches -- https://code.launchpad.net/~openobject-italia-core-devs/openobject-italia/l10n_it_patches/+merge/60050 Your team OpenERP Italia core devs is requested to review the proposed merge of lp:~openobject-italia-core-devs/openobject-italia/l10n_it_patches into lp:openobject-italia.
=== added directory 'l10n_it_patches' === added file 'l10n_it_patches/AUTHORS.txt' --- l10n_it_patches/AUTHORS.txt 1970-01-01 00:00:00 +0000 +++ l10n_it_patches/AUTHORS.txt 2011-05-05 12:01:36 +0000 @@ -0,0 +1,2 @@ +Matteo Grolla <[email protected]> +Marco Marchiori <[email protected]> \ No newline at end of file === added file 'l10n_it_patches/__init__.py' --- l10n_it_patches/__init__.py 1970-01-01 00:00:00 +0000 +++ l10n_it_patches/__init__.py 2011-05-05 12:01:36 +0000 @@ -0,0 +1,23 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010 Italian OpenERP Community (<http://www.openerp-italia.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## +# import account_voucher #not working +import invoice +import sequence === added file 'l10n_it_patches/__openerp__.py' --- l10n_it_patches/__openerp__.py 1970-01-01 00:00:00 +0000 +++ l10n_it_patches/__openerp__.py 2011-05-05 12:01:36 +0000 @@ -0,0 +1,73 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2010 OpenERP Italian Community (<http://www.openerp-italia.org>). +# All Rights Reserved +# $Id$ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## +{ + 'name': 'Italian Localisation Patches, Beta', + 'version': '0.1', + 'category': 'Localisation/Italy', + 'description': """Italian localization module (beta version) that covers + + -Invoice taxes: + legend: + TA(l) TaxAmount for line l + TBA(l) TaxBaseAmount for line l + TA(t) TaxAmount for tax t + TBA(t) TaxBaseAmount for tax t + + default behaviour: + openerp by default calculates taxes line by line as a function of line tax base amount + and then groups these amounts by tax. + TA(l) = f(TBA(l) + TA(t) = sum( round( TA(l) ) ) /sum on lines l to which tax t is applied + this module behaviour: + In Italy the correct way to compute taxes is to first compute the tax base amount TBA(t) + and the compute TA as a function of TBA(t) + TBA(t) = sum( TBA(t) ) /sum on lines l to which tax t is applied + TA(t) = f( TBA(t) ) + the result difference is small but important + + -Sequences: + adds a placeholder that you can use in the prefix of sequences + placeholder meaning + fy-code the code of the fiscalyear of the registration + (Eg. Administration->Configuration->Sequences->Sequences Prefix = SAJ/%(fy-code)s/) + reason for the new placeholder + if you use %(year)s on a sequence prefix, for example Prefix = SAJ/%(year)s/)), and register on january 2011 + an invoice dated december 2010 the journal entry number will be SAJ/2011/<number> + You can use sequences associated with fiscalyear and write a hardcoded string describing the fiscal period but + this placeholder is a practical default either when you are using a single sequence for journal or a sequence + per fiscal year per journal + + see also: + https://bugs.launchpad.net/openobject-server/+bug/504720 + """, + 'author': 'OpenERP Italian Community', + 'website': 'http://www.openerp-italia.org', + 'license': 'AGPL-3', + "depends" : ['account', 'account_voucher'], + "init_xml" : [], + "update_xml" : [], + "demo_xml" : [], + "active": False, + "installable": True +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + === added file 'l10n_it_patches/account_voucher.py' --- l10n_it_patches/account_voucher.py 1970-01-01 00:00:00 +0000 +++ l10n_it_patches/account_voucher.py 2011-05-05 12:01:36 +0000 @@ -0,0 +1,59 @@ +import time +from osv import fields, osv + +# Problem with original class: https://bugs.launchpad.net/openobject-addons/+bug/761918 +# this class is currently broken, +# if the bug is important to you subscribe it +# +class account_voucher(osv.osv): + _inherit = "account.voucher" + + def compute_tax(self, cr, uid, ids, context=None): + tax_pool = self.pool.get('account.tax') + partner_pool = self.pool.get('res.partner') + position_pool = self.pool.get('account.fiscal.position') + voucher_line_pool = self.pool.get('account.voucher.line') + voucher_pool = self.pool.get('account.voucher') + if context is None: context = {} + + for voucher in voucher_pool.browse(cr, uid, ids, context=context): + voucher_amount = 0.0 + for line in voucher.line_ids: + #voucher_amount += line.untax_amount or line.amount + voucher_amount += line.amount + line.amount = line.amount + voucher_line_pool.write(cr, uid, [line.id], {'amount':line.amount, 'untax_amount':line.untax_amount}) + + if not voucher.tax_id: + self.write(cr, uid, [voucher.id], {'amount':voucher_amount, 'tax_amount':0.0}) + continue + + tax = [tax_pool.browse(cr, uid, voucher.tax_id.id, context=context)] + partner = partner_pool.browse(cr, uid, voucher.partner_id.id, context=context) or False + taxes = position_pool.map_tax(cr, uid, partner and partner.property_account_position or False, tax) + tax = tax_pool.browse(cr, uid, taxes, context=context) + + total = voucher_amount + total_tax = 0.0 + + if not tax[0].price_include: + for tax_line in tax_pool.compute_all(cr, uid, tax, voucher_amount, 1).get('taxes', []): + total_tax += tax_line.get('amount', 0.0) + total += total_tax + else: + for line in voucher.line_ids: + line_total = 0.0 + line_tax = 0.0 + + #for tax_line in tax_pool.compute_all(cr, uid, tax, line.untax_amount or line.amount, 1).get('taxes', []): + for tax_line in tax_pool.compute_all(cr, uid, tax, line.amount, 1).get('taxes', []): + line_tax += tax_line.get('amount', 0.0) + line_total += tax_line.get('price_unit') + total_tax += line_tax + untax_amount = line.untax_amount or (line_total) + voucher_line_pool.write(cr, uid, [line.id], {'amount':line.amount, 'untax_amount':untax_amount}) + + self.write(cr, uid, [voucher.id], {'amount':total, 'tax_amount':total_tax}) + return True + +account_voucher() \ No newline at end of file === added file 'l10n_it_patches/invoice.py' --- l10n_it_patches/invoice.py 1970-01-01 00:00:00 +0000 +++ l10n_it_patches/invoice.py 2011-05-05 12:01:36 +0000 @@ -0,0 +1,66 @@ +import time +from osv import fields, osv + + +class account_invoice_tax(osv.osv): + _inherit = "account.invoice.tax" + + + # FIXME calcolare correttamente con tax inlcuded in price + # TODO analizzare con tasse complesse che includono child taxes + # + def compute(self, cr, uid, invoice_id, context=None): + tax_grouped = {} + tax_obj = self.pool.get('account.tax') + cur_obj = self.pool.get('res.currency') + inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context=context) + cur = inv.currency_id + company_currency = inv.company_id.currency_id.id + + for line in inv.invoice_line: + for tax in tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id)['taxes']: + val={} + val['invoice_id'] = inv.id + val['name'] = tax['name'] + val['amount'] = tax['amount'] + val['price'] = (line.price_unit* (1-(line.discount or 0.0)/100.0)) * line.quantity + val['manual'] = False + val['sequence'] = tax['sequence'] + val['base'] = tax['price_unit'] * line['quantity'] + for line_tax in line.invoice_line_tax_id: + if tax["id"] == line_tax.id: + val['tax'] = [line_tax] + + if inv.type in ('out_invoice','in_invoice'): + val['base_code_id'] = tax['base_code_id'] + val['tax_code_id'] = tax['tax_code_id'] + val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['account_id'] = tax['account_collected_id'] or line.account_id.id + else: + val['base_code_id'] = tax['ref_base_code_id'] + val['tax_code_id'] = tax['ref_tax_code_id'] + val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['account_id'] = tax['account_paid_id'] or line.account_id.id + + key = (val['tax_code_id'], val['base_code_id'], val['account_id']) + if not key in tax_grouped: + tax_grouped[key] = val + else: + tax_grouped[key]['amount'] += val['amount'] + tax_grouped[key]['price'] += val['price'] + tax_grouped[key]['base'] += val['base'] + tax_grouped[key]['base_amount'] += val['base_amount'] + tax_grouped[key]['tax_amount'] += val['tax_amount'] + + for t in tax_grouped.values(): + t['price'] = cur_obj.round(cr, uid, cur, t['price']) + amount = tax_obj.compute_all(cr, uid, t['tax'], t['price'], 1)["taxes"][0]["amount"] #MG + t['amount'] = cur_obj.round(cr, uid, cur, amount) + t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount']) + t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount']) #TODO anche il tax amount deve essere calcolato correttamente, dove viene usato? + return tax_grouped + +account_invoice_tax() + === added file 'l10n_it_patches/sequence.py' --- l10n_it_patches/sequence.py 1970-01-01 00:00:00 +0000 +++ l10n_it_patches/sequence.py 2011-05-05 12:01:36 +0000 @@ -0,0 +1,75 @@ +import time +from osv import fields,osv +import pooler + +# TODO: refactoring (help appreciated) +# in a standard installation +# this class C extends class B defined in module account/sequence.py +# B extends class A defined in module base/ir/ir_sequence.py +# +# the code would be cleaner and more mantainable if I managed to do this +# this class C extend class A +# class B extend C +# this way I could define only methods _process() and get_id() (with the content of parent_get_id() ) +class ir_sequence(osv.osv): + _inherit = 'ir.sequence' + + def _process(self, cr, s, context=None): #MG + fYearCode = 'ND' + if context and context.get('fiscalyear_id', False): #MG + fYearId = context.get('fiscalyear_id') + cr.execute('select code from account_fiscalyear where id =%s', (fYearId,)) + res = cr.dictfetchone() + fYearCode = res['code'] + return (s or '') % { + 'year':time.strftime('%Y'), + 'fy-code':fYearCode, #MG + 'month': time.strftime('%m'), + 'day':time.strftime('%d'), + 'y': time.strftime('%y'), + 'doy': time.strftime('%j'), + 'woy': time.strftime('%W'), + 'weekday': time.strftime('%w'), + 'h24': time.strftime('%H'), + 'h12': time.strftime('%I'), + 'min': time.strftime('%M'), + 'sec': time.strftime('%S'), + } + + def parent_get_id(self, cr, uid, sequence_id, test='id', context=None): + assert test in ('code','id') + company_id = self.pool.get('res.users').read(cr, uid, uid, ['company_id'], context=context)['company_id'][0] or None + cr.execute('''SELECT id, number_next, prefix, suffix, padding + FROM ir_sequence + WHERE %s=%%s + AND active=true + AND (company_id = %%s or company_id is NULL) + ORDER BY company_id, id + FOR UPDATE NOWAIT''' % test, + (sequence_id, company_id)) + res = cr.dictfetchone() + if res: + cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=true', (res['id'],)) + if res['number_next']: + return self._process(cr, res['prefix'], context) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(cr, res['suffix'], context) #MG + else: + return self._process(cr, res['prefix'], context) + self._process(cr, res['suffix'], context) #MG + return False + + def get_id(self, cr, uid, sequence_id, test='id', context=None): + if context is None: + context = {} + cr.execute('select id from ir_sequence where ' + + test + '=%s and active=%s', (sequence_id, True,)) + res = cr.dictfetchone() + if res: + for line in self.browse(cr, uid, res['id'], + context=context).fiscal_ids: + if line.fiscalyear_id.id == context.get('fiscalyear_id', False): + return self.parent_get_id(cr, uid, + line.sequence_id.id, + test="id", + context=context) + return self.parent_get_id(cr, uid, sequence_id, test, + context=context) +ir_sequence() \ No newline at end of file === added directory 'l10n_it_patches/tests' === added file 'l10n_it_patches/tests/tests.txt' --- l10n_it_patches/tests/tests.txt 1970-01-01 00:00:00 +0000 +++ l10n_it_patches/tests/tests.txt 2011-05-05 12:01:36 +0000 @@ -0,0 +1,107 @@ +Manual tests on tax calculation (should write automated tests) + +1) arrotondamento IVA ordinaria 20% +fattura VENDJ/2011/016 +IVA20 12,29 2,46 +IVA20 12,29 2,46 +IVA20 12,29 2,46 +IVA10 + +IVA10 + +IVA10 + +IVA10 + +IVA10 + +IVA10 + +IVA10 + +IVA10 + +IVA10 + +somma 36,87 7,38 + + + +IVA10 0 0 +IVA20 36,87 7,37 + + + + + 36,87 7,37 +L'IVA corretta è 7,37, e cioè il 20% del totale imponibile al 20% + +2) arrotondamento IVA ordinaria e agevolata 4% +fattura VENDJ/2011/018 +IVA4 12,33 0,49 +IVA20 12,29 2,46 +IVA20 12,29 2,46 +IVA20 12,29 2,46 +IVA4 12,33 0,49 +IVA4 12,33 0,49 +IVA20 12,29 2,46 +IVA20 12,29 2,46 +IVA20 12,29 2,46 +IVA20 + +IVA20 + +IVA20 + + + + +somma 110,73 16,23 + + + +IVA4 36,99 1,48 +IVA20 73,74 14,75 + + + +TOTALE IVA 110,73 16,23 + +l'IVA viene correttamente ripartita fra IVA 4 ed IVA 20 + +arrotondamento IVA ordinaria 20% ed agevolata 10% + +fattura VENDJ/2011/019 +IVA20 12,29 2,46 +IVA20 12,29 2,46 +IVA20 12,29 2,46 +IVA10 10,56 1,06 +IVA10 10,56 1,06 +IVA10 10,56 1,06 +IVA10 + +IVA10 + +IVA10 + +IVA10 + +IVA10 + +IVA10 + + + + +somma 68,55 10,56 + + + +IVA10 31,68 3,17 +IVA20 36,87 7,37 + + + +TOTALE IVA 68,55 10,54 + +L'IVA viene correttamente calcolata ed indicata secondo le aliquote
_______________________________________________ Mailing list: https://launchpad.net/~openobject-italia-core-devs Post to : [email protected] Unsubscribe : https://launchpad.net/~openobject-italia-core-devs More help : https://help.launchpad.net/ListHelp

