← Back to team overview

openobject-italia-core-devs team mailing list archive

lp:~openobject-italia-core-devs/openobject-italia/l10n_it_patches into lp:openobject-italia

 

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 <magrolla@xxxxxxxx>
+Marco Marchiori <marcomarkiori@xxxxxxxxx>
\ 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


Follow ups