~new-report-intrastat-team/new-report-intrastat/6.1

« back to all changes in this revision

Viewing changes to l10n_fr_intrastat_product/intrastat_product.py

  • Committer: Alexis de Lattre
  • Date: 2012-06-05 13:04:00 UTC
  • Revision ID: alexis@via.ecp.fr-20120605130400-7qsgt1bsuemzxa4c
Add option "is_accessory_cost" on product.template :
  If the invoice has is_accessory_cost services but no regular product -> DES
  If the invoice has is_accessory_cost services and regular product -> added to the cost of products in DEB

Now allows "pricelist for statistical value" which is not in EUR (the currency conversion will be made from the pricelist currency to EUR)

Usability improvements :
- Order for DEB and DES tree view : "the more recent at the top"
- distinction between "Information to declare" and "Additionnal information" in intrastat lines

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
##############################################################################
3
3
#
4
4
#    Report intrastat product module for OpenERP
5
 
#    Copyright (C) 2009-2011 Akretion (http://www.akretion.com). All Rights Reserved.
 
5
#    Copyright (C) 2009-2012 Akretion (http://www.akretion.com). All Rights Reserved.
6
6
#    @author Alexis de Lattre <alexis.delattre@akretion.com>
7
7
#
8
8
#    This program is free software: you can redistribute it and/or modify
25
25
from datetime import datetime
26
26
from dateutil.relativedelta import relativedelta
27
27
from tools.translate import _
28
 
 
 
28
import decimal_precision as dp
29
29
 
30
30
class report_intrastat_product(osv.osv):
31
31
    _name = "report.intrastat.product"
32
32
    _description = "Intrastat report for products"
33
33
    _rec_name = "start_date"
34
 
    _order = "start_date, type"
 
34
    _order = "start_date desc, type"
35
35
 
36
36
 
37
37
    def _compute_numbers(self, cr, uid, ids, name, arg, context=None):
54
54
        return self.pool.get('report.intrastat.product').search(cr, uid, [('intrastat_line_ids', 'in', ids)], context=context)
55
55
 
56
56
    _columns = {
57
 
        'company_id': fields.many2one('res.company', 'Company', required=True, states={'done':[('readonly',True)]}, help="Related company."),
58
 
        'start_date': fields.date('Start date', required=True, states={'done':[('readonly',True)]}, help="Start date of the declaration. Must be the first day of a month."),
59
 
        'end_date': fields.function(_compute_end_date, method=True, type='date', string='End date', store={
60
 
            'report.intrastat.product': (lambda self, cr, uid, ids, c={}: ids, ['start_date'], 10),
61
 
                }, help="End date for the declaration. Is the last day of the month of the start date."),
 
57
        'company_id': fields.many2one('res.company', 'Company', required=True,
 
58
            states={'done':[('readonly',True)]}, help="Related company."),
 
59
        'start_date': fields.date('Start date', required=True,
 
60
            states={'done':[('readonly',True)]},
 
61
            help="Start date of the declaration. Must be the first day of a month."),
 
62
        'end_date': fields.function(_compute_end_date, method=True, type='date',
 
63
            string='End date', store={
 
64
                'report.intrastat.product': (lambda self, cr, uid, ids, c={}: ids, ['start_date'], 10),
 
65
                },
 
66
            help="End date for the declaration. Is the last day of the month of the start date."),
62
67
        'type': fields.selection([
63
 
            ('import', 'Import'),
64
 
            ('export', 'Export')
65
 
            ], 'Type', required=True, states={'done':[('readonly',True)]}, help="Select the type of DEB."),
 
68
                ('import', 'Import'),
 
69
                ('export', 'Export')
 
70
            ], 'Type', required=True, states={'done':[('readonly',True)]},
 
71
            help="Select the type of DEB."),
66
72
        'obligation_level' : fields.selection([
67
 
            ('detailed', 'Detailed'),
68
 
            ('simplified', 'Simplified')
69
 
            ], 'Obligation level', required=True, states={'done':[('readonly',True)]}, help="Your obligation level for a certain type of DEB (Import or Export) depends on the total value that you export or import per year. Note that the obligation level 'Simplified' doesn't exist for an Import DEB."),
70
 
        'intrastat_line_ids': fields.one2many('report.intrastat.product.line', 'parent_id', 'Report intrastat product lines', states={'done':[('readonly',True)]}),
71
 
        'num_lines': fields.function(_compute_numbers, method=True, type='integer', multi='numbers', string='Number of lines', store={
72
 
            'report.intrastat.product.line': (_get_intrastat_from_product_line, ['parent_id'], 20),
73
 
        }, help="Number of lines in this declaration."),
74
 
        'total_amount': fields.function(_compute_numbers, method=True, digits=(16,0), multi='numbers', string='Total amount', store={
75
 
            'report.intrastat.product.line': (_get_intrastat_from_product_line, ['amount_company_currency', 'parent_id'], 20),
76
 
            }, help="Total amount in company currency of the declaration."),
77
 
        'total_fiscal_amount': fields.function(_compute_total_fiscal_amount, method=True, digits=(16,0), string='Total fiscal amount', store={
78
 
            'report.intrastat.product.line': (_get_intrastat_from_product_line, ['amount_company_currency', 'parent_id'], 20),
79
 
            }, help="Total fiscal amount in company currency of the declaration. This is the total amount that is displayed on the Prodouane website."),
80
 
        'currency_id': fields.related('company_id', 'currency_id', readonly=True, type='many2one', relation='res.currency', string='Currency'),
 
73
                ('detailed', 'Detailed'),
 
74
                ('simplified', 'Simplified')
 
75
            ], 'Obligation level', required=True,
 
76
            states={'done':[('readonly',True)]},
 
77
            help="Your obligation level for a certain type of DEB (Import or Export) depends on the total value that you export or import per year. Note that the obligation level 'Simplified' doesn't exist for an Import DEB."),
 
78
        'intrastat_line_ids': fields.one2many('report.intrastat.product.line',
 
79
            'parent_id', 'Report intrastat product lines',
 
80
            states={'done':[('readonly',True)]}),
 
81
        'num_lines': fields.function(_compute_numbers, method=True, type='integer',
 
82
            multi='numbers', string='Number of lines', store={
 
83
                'report.intrastat.product.line': (_get_intrastat_from_product_line, ['parent_id'], 20),
 
84
            },
 
85
            help="Number of lines in this declaration."),
 
86
        'total_amount': fields.function(_compute_numbers, method=True,
 
87
            digits_compute=dp.get_precision('Account'), multi='numbers',
 
88
            string='Total amount', store={
 
89
                'report.intrastat.product.line': (_get_intrastat_from_product_line, ['amount_company_currency', 'parent_id'], 20),
 
90
            },
 
91
            help="Total amount in company currency of the declaration."),
 
92
        'total_fiscal_amount': fields.function(_compute_total_fiscal_amount,
 
93
            method=True, digits_compute=dp.get_precision('Account'),
 
94
            string='Total fiscal amount', store={
 
95
                'report.intrastat.product.line': (_get_intrastat_from_product_line, ['amount_company_currency', 'parent_id'], 20),
 
96
            },
 
97
            help="Total fiscal amount in company currency of the declaration. This is the total amount that is displayed on the Prodouane website."),
 
98
        'currency_id': fields.related('company_id', 'currency_id', readonly=True,
 
99
            type='many2one', relation='res.currency', string='Currency'),
81
100
        'state' : fields.selection([
82
 
            ('draft','Draft'),
83
 
            ('done','Done'),
84
 
        ], 'State', select=True, readonly=True, help="State of the declaration. When the state is set to 'Done', the parameters become read-only."),
85
 
        'date_done' : fields.datetime('Date done', readonly=True, help="Last date when the intrastat declaration was converted to 'Done' state."),
86
 
        'notes' : fields.text('Notes', help="You can add some comments here if you want."),
 
101
                ('draft','Draft'),
 
102
                ('done','Done'),
 
103
            ], 'State', select=True, readonly=True,
 
104
            help="State of the declaration. When the state is set to 'Done', the parameters become read-only."),
 
105
        'date_done' : fields.datetime('Date done', readonly=True,
 
106
            help="Last date when the intrastat declaration was converted to 'Done' state."),
 
107
        'notes' : fields.text('Notes',
 
108
            help="You can add some comments here if you want."),
87
109
    }
88
110
 
89
111
    _defaults = {
136
158
        return res_id
137
159
 
138
160
    def create_intrastat_product_lines(self, cr, uid, ids, intrastat, parent_obj, parent_values, context=None):
 
161
        """This function is called for each invoice and for each picking"""
139
162
        #print "create_intrastat_product_line ids=", ids
140
163
 
141
164
        if len(ids) != 1: raise osv.except_osv(_('Error :'), 'Hara kiri in build_intrastat_product_line')
154
177
            browse_on = parent_obj.invoice_line
155
178
            parent_name = parent_obj.number
156
179
            product_line_ref_field = 'invoice_id'
 
180
            currency_obj = parent_obj.currency_id
157
181
        elif parent_obj._name == 'stock.picking':
158
182
            src = 'picking'
159
183
            browse_on = parent_obj.move_lines
160
184
            parent_name = parent_obj.name
161
185
            product_line_ref_field = 'picking_id'
 
186
            currency_obj = intrastat.company_id.statistical_pricelist_id.currency_id
162
187
        else: raise osv.except_osv(_('Error :'), 'The function build_intrastat_product_lines() should have parent_obj as invoice or picking')
163
188
 
164
189
        lines_to_create = []
 
190
        total_invoice_cur_accessory_cost = 0.0
 
191
        total_invoice_cur_product_value = 0.0
165
192
        for line in browse_on:
166
193
            if src == 'invoice':
167
194
                line_qty = line.quantity
169
196
            elif src == 'picking':
170
197
                line_qty = line.product_qty
171
198
                source_uom = line.product_uom
172
 
            # We don't do anything when there is no product_id... this may be a problem...
173
199
 
 
200
            # We don't do anything when there is no product_id...
 
201
            # this may be a problem... but i think a raise would be too violent
174
202
            if not line.product_id:
175
203
                continue
176
204
 
177
 
            if line.product_id.type not in ('product', 'consu'):
178
 
                continue
179
 
 
180
205
            if line.product_id.exclude_from_intrastat:
181
206
                continue
182
207
 
 
208
            if not line_qty:
 
209
                continue
 
210
 
 
211
            # If type = "service" and is_accessory_cost=True, then we keep
 
212
            # the line (it will be skipped later on)
 
213
            if line.product_id.type not in ('product', 'consu') and not line.product_id.is_accessory_cost:
 
214
                continue
 
215
 
183
216
            if src == 'picking':
184
217
                if line.state <> 'done':
185
218
                    continue
188
221
                if parent_obj.type == 'out' and line.location_dest_id.usage == 'internal':
189
222
                    continue
190
223
 
191
 
            if not line_qty:
192
 
                continue
193
 
 
194
224
            if src == 'invoice':
195
225
                skip_this_line = False
196
226
                for line_tax in line.invoice_line_tax_id:
198
228
                        skip_this_line = True
199
229
                if skip_this_line:
200
230
                    continue
201
 
                amount_invoice_currency_to_write = line.price_subtotal
202
 
                invoice_currency_id_to_write = parent_obj.currency_id.id
203
 
                if parent_obj.currency_id.name <> 'EUR':
204
 
                    context['date'] = parent_obj.date_invoice
205
 
# TODO : add invoice_currency, also in add loop, WARN picking
206
 
                    amount_company_currency_to_write = self.pool.get('res.currency').compute(cr, uid, parent_obj.currency_id.id, intrastat.company_id.currency_id.id, line.price_subtotal, context=context)
207
 
                else:
208
 
                    amount_company_currency_to_write = line.price_subtotal
 
231
                if line.product_id.is_accessory_cost and line.product_id.type == 'service':
 
232
                    total_invoice_cur_accessory_cost += line.price_subtotal
 
233
                    continue
 
234
                # END OF "continue" instructions
 
235
                ## AFTER THIS POINT, we are sure to have real products that have to be declared to DEB
 
236
                amount_product_value_inv_cur_to_write = line.price_subtotal
 
237
                total_invoice_cur_product_value += line.price_subtotal
 
238
                invoice_currency_id_to_write = currency_obj.id
 
239
 
209
240
            elif src == 'picking':
210
 
                invoice_currency_id_to_write = False
211
 
                amount_invoice_currency_to_write = False
 
241
                invoice_currency_id_to_write = currency_obj.id
212
242
                unit_stat_price = self.pool.get('product.pricelist').price_get(cr, uid, [intrastat.company_id.statistical_pricelist_id.id], line.product_id.id, 1.0)[intrastat.company_id.statistical_pricelist_id.id]
213
243
                if not unit_stat_price:
214
244
                    raise osv.except_osv(_('Error :'), _("The Pricelist for statistical value '%s' that is set for the company '%s' gives a price of 0 for the product '%s'.") %(intrastat.company_id.statistical_pricelist_id.name, intrastat.company_id.name, line.product_id.name))
215
245
                else:
216
 
                    amount_company_currency_to_write = unit_stat_price * line_qty
 
246
                    amount_product_value_inv_cur_to_write = unit_stat_price * line_qty
217
247
 
218
248
            if not parent_values['is_fiscal_only']:
219
249
 
321
351
                    and line_to_create.get('product_country_origin_id', False) == product_country_origin_id_to_write:
322
352
                    create_new_line = False
323
353
                    line_to_create['quantity'] += quantity_to_write
324
 
                    line_to_create['amount_company_currency'] += amount_company_currency_to_write
 
354
#                    line_to_create['amount_company_currency'] += amount_company_currency_to_write
325
355
                    line_to_create['weight'] += weight_to_write
326
 
                    line_to_create['amount_invoice_currency'] += amount_invoice_currency_to_write
 
356
#                    line_to_create['amount_invoice_currency'] += amount_invoice_currency_to_write
 
357
                    line_to_create['amount_product_value_inv_cur'] += amount_product_value_inv_cur_to_write
327
358
                    break
328
359
            if create_new_line == True:
329
360
                lines_to_create.append({
336
367
                    'intrastat_code': intrastat_code_to_write,
337
368
                    'intrastat_code_id': intrastat_code_id_to_write,
338
369
                    'weight': weight_to_write,
339
 
                    'amount_company_currency': amount_company_currency_to_write,
 
370
#                    'amount_company_currency': amount_company_currency_to_write,
340
371
                    'product_country_origin_id': product_country_origin_id_to_write,
341
372
                    'transport': parent_values['transport_to_write'],
342
373
                    'department': parent_values['department_to_write'],
345
376
                    'transaction_code': parent_values['transaction_code_to_write'],
346
377
                    'partner_id': parent_values['partner_id_to_write'],
347
378
                    'invoice_currency_id': invoice_currency_id_to_write,
348
 
                    'amount_invoice_currency': amount_invoice_currency_to_write,
 
379
#                    'amount_invoice_currency': amount_invoice_currency_to_write,
 
380
                    'amount_product_value_inv_cur': amount_product_value_inv_cur_to_write,
349
381
                    'is_fiscal_only': parent_values['is_fiscal_only'],
350
382
                })
351
383
        # End of the loop on invoice/picking lines
352
384
 
353
 
        # Why do I manage the Partner VAT number only here and not earlier in the code ?
 
385
        # Why do I manage the Partner VAT number only here and not earlier
 
386
        # in the code ?
354
387
        # Because, if I sell to a physical person in the EU with VAT, then
355
388
        # the corresponding partner will not have a VAT number, and the entry
356
 
        # will be skipped because line_tax.exclude_from_intrastat_if_present is always True
 
389
        # will be skipped because line_tax.exclude_from_intrastat_if_present
 
390
        # is always True
357
391
        # So we should not block with a raise before the end of the loop on the
358
392
        # invoice/picking lines
359
393
        if lines_to_create:
379
413
 
380
414
        for line_to_create in lines_to_create:
381
415
            line_to_create['partner_vat'] = parent_values['partner_vat_to_write']
 
416
 
 
417
            if src == 'picking':
 
418
                context['date'] = parent_obj.date_done # for currency conversion
 
419
                line_to_create['amount_accessory_cost_inv_cur'] = 0
 
420
            elif src == 'invoice':
 
421
                context['date'] = parent_obj.date_invoice # for currency conversion
 
422
                if not total_invoice_cur_accessory_cost:
 
423
                    line_to_create['amount_accessory_cost_inv_cur'] = 0
 
424
                else:
 
425
                    # The accessory costs are added at the pro-rata of value
 
426
                    line_to_create['amount_accessory_cost_inv_cur'] = total_invoice_cur_accessory_cost * line_to_create['amount_product_value_inv_cur'] / total_invoice_cur_product_value
 
427
 
 
428
            line_to_create['amount_invoice_currency'] = line_to_create['amount_product_value_inv_cur'] + line_to_create['amount_accessory_cost_inv_cur']
 
429
 
 
430
 
 
431
            # We do currency conversion NOW
 
432
            if currency_obj.name != 'EUR':
 
433
                line_to_create['amount_company_currency'] = self.pool.get('res.currency').compute(cr, uid, currency_obj.id, intrastat.company_id.currency_id.id, line_to_create['amount_invoice_currency'], context=context)
 
434
            else:
 
435
                line_to_create['amount_company_currency'] = line_to_create['amount_invoice_currency']
 
436
            # We round
382
437
            line_to_create['amount_company_currency'] = int(round(line_to_create['amount_company_currency']))
383
438
            if line_to_create['amount_company_currency'] == 0:
384
439
                # p20 of the BOD : lines with value rounded to 0 mustn't be declared
386
441
            for value in ['quantity', 'weight']: # These 2 fields are char
387
442
                if line_to_create[value]:
388
443
                    line_to_create[value] = str(int(round(line_to_create[value], 0)))
389
 
            line_to_create['amount_invoice_currency'] = int(round(line_to_create['amount_invoice_currency']))
390
444
            line_obj.create(cr, uid, line_to_create, context=context)
391
445
 
392
446
        return True
547
601
        # Check pricelist for stat value
548
602
        if not intrastat.company_id.statistical_pricelist_id:
549
603
            raise osv.except_osv(_('Error :'), _("You must select a 'Pricelist for statistical value' for the company %s.") %intrastat.company_id.name)
550
 
        elif intrastat.company_id.statistical_pricelist_id.currency_id.name <> 'EUR':
551
 
            raise osv.except_osv(_('Error :'), _("The 'Pricelist for statistical value' that you selected (%s) for the company '%s' is in %s currency and should be in EUR.") %(intrastat.company_id.statistical_pricelist_id.name, intrastat.company_id.name, intrastat.company_id.statistical_pricelist_id.currency_id.name))
552
604
 
553
605
        pick_obj = self.pool.get('stock.picking')
554
606
        pick_type = False
559
611
        if intrastat.type == 'export':
560
612
            pick_type = 'out'
561
613
            exclude_field = 'sale_id'
562
 
        # TODO : add criteria on company !!!
563
614
        picking_ids = pick_obj.search(cr, uid, [
564
615
            ('type', '=', pick_type),
565
616
            ('date_done', '<=', intrastat.end_date),
566
617
            ('date_done', '>=', intrastat.start_date),
567
618
            ('invoice_state', '=', 'none'),
 
619
            ('company_id', '=', intrastat.company_id.id),
568
620
            (exclude_field, '=', False),
569
621
            ('state', 'not in', ('draft', 'waiting', 'confirmed', 'assigned', 'cancel'))
570
622
        ], order='date_done', context=context)
799
851
        # tree view when the value is False (if weight is an integer, a False value would
800
852
        # be displayed as 0), that's why weight is a char !
801
853
        'weight': fields.char('Weight', size=10, states={'done':[('readonly',True)]}),
802
 
        'amount_invoice_currency': fields.integer('Fiscal value in invoice currency', readonly=True),
803
 
        'amount_company_currency': fields.integer('Fiscal value in company currency', required=True, states={'done':[('readonly',True)]}),
 
854
        'amount_company_currency': fields.integer('Fiscal value in company currency',
 
855
            required=True, states={'done':[('readonly',True)]},
 
856
            help="Amount in company currency to write in the declaration. Amount in company currency = amount in invoice currency converted to company currency with the rate of the invoice date (for pickings : with the rate of the 'date done') and rounded at 0 digits"),
 
857
        'amount_invoice_currency': fields.float('Fiscal value in invoice currency',
 
858
            digits_compute=dp.get_precision('Account'), readonly=True,
 
859
            help="Amount in invoice currency = amount of product value in invoice currency + amount of accessory cost in invoice currency (not rounded)"),
 
860
        'amount_accessory_cost_inv_cur': fields.float(
 
861
            'Amount of accessory costs in invoice currency',
 
862
            digits_compute=dp.get_precision('Account'), readonly=True,
 
863
            help="Amount of accessory costs in invoice currency = total amount of accessory costs of the invoice broken down into each product line at the pro-rata of the value"),
 
864
        'amount_product_value_inv_cur': fields.float(
 
865
            'Amount of product value in invoice currency',
 
866
            digits_compute=dp.get_precision('Account'), readonly=True,
 
867
            help="Amount of product value in invoice currency. For invoices, it is the amount of the invoice line or group of invoice lines. For pickings, it is the value of the product given by the pricelist for statistical value of the company."),
804
868
        'invoice_currency_id': fields.many2one('res.currency', "Invoice currency", readonly=True),
805
869
        'product_country_origin_id' : fields.many2one('res.country', 'Product country of origin', states={'done':[('readonly',True)]}),
806
870
        'product_country_origin_code' : fields.related('product_country_origin_id', 'code', type='string', relation='res.country', string='Product country of origin', readonly=True),