summaryrefslogtreecommitdiff
path: root/addons/l10n_lu/scripts/tax2csv.py
blob: 2a2da358b4bcba8c8d6f74019a57175dfa1a629d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
from collections import OrderedDict

import xlrd
from odoo.tools import pycompat

def _is_true(s):
    return s not in ('F', 'False', 0, '', None, False)


class LuxTaxGenerator:

    def __init__(self, filename):
        self.workbook = xlrd.open_workbook('tax.xls')
        self.sheet_info = \
            self.workbook.sheet_by_name('INFO')
        self.sheet_taxes = \
            self.workbook.sheet_by_name('TAXES')
        self.sheet_tax_codes = \
            self.workbook.sheet_by_name('TAX.CODES')
        self.sheet_fiscal_pos_map = \
            self.workbook.sheet_by_name('FISCAL.POSITION.MAPPINGS')
        self.suffix = self.sheet_info.cell_value(4, 2)

    def iter_tax_codes(self):
        keys = [c.value for c in self.sheet_tax_codes.row(0)]
        yield keys
        for i in range(1, self.sheet_tax_codes.nrows):
            row = (c.value for c in self.sheet_tax_codes.row(i))
            d = OrderedDict(zip(keys, row))
            d['sign'] = int(d['sign'])
            d['sequence'] = int(d['sequence'])
            yield d

    def iter_taxes(self):
        keys = [c.value for c in self.sheet_taxes.row(0)]
        yield keys
        for i in range(1, self.sheet_taxes.nrows):
            row = (c.value for c in self.sheet_taxes.row(i))
            yield OrderedDict(zip(keys, row))

    def iter_fiscal_pos_map(self):
        keys = [c.value for c in self.sheet_fiscal_pos_map.row(0)]
        yield keys
        for i in range(1, self.sheet_fiscal_pos_map.nrows):
            row = (c.value for c in self.sheet_fiscal_pos_map.row(i))
            yield OrderedDict(zip(keys, row))

    def tax_codes_to_csv(self):
        writer = pycompat.csv_writer(open('account.tax.code.template-%s.csv' %
                                 self.suffix, 'wb'))
        tax_codes_iterator = self.iter_tax_codes()
        keys = next(tax_codes_iterator)
        writer.writerow(keys)

        # write structure tax codes
        tax_codes = {}  # code: id
        for row in tax_codes_iterator:
            tax_code = row['code']
            if tax_code in tax_codes:
                raise RuntimeError('duplicate tax code %s' % tax_code)
            tax_codes[tax_code] = row['id']
            writer.writerow([pycompat.to_text(v) for v in row.values()])

        # read taxes and add leaf tax codes
        new_tax_codes = {}  # id: parent_code

        def add_new_tax_code(tax_code_id, new_name, new_parent_code):
            if not tax_code_id:
                return
            name, parent_code = new_tax_codes.get(tax_code_id, (None, None))
            if parent_code and parent_code != new_parent_code:
                raise RuntimeError('tax code "%s" already exist with '
                                   'parent %s while trying to add it with '
                                   'parent %s' %
                                   (tax_code_id, parent_code, new_parent_code))
            else:
                new_tax_codes[tax_code_id] = (new_name, new_parent_code)

        taxes_iterator = self.iter_taxes()
        next(taxes_iterator)
        for row in taxes_iterator:
            if not _is_true(row['active']):
                continue
            if row['child_depend'] and row['amount'] != 1:
                raise RuntimeError('amount must be one if child_depend '
                                   'for %s' % row['id'])
            # base parent
            base_code = row['BASE_CODE']
            if not base_code or base_code == '/':
                base_code = 'NA'
            if base_code not in tax_codes:
                raise RuntimeError('undefined tax code %s' % base_code)
            if base_code != 'NA':
                if row['child_depend']:
                    raise RuntimeError('base code specified '
                                       'with child_depend for %s' % row['id'])
            if not row['child_depend']:
                # ... in lux, we have the same code for invoice and refund
                if base_code != 'NA':
                    assert row['base_code_id:id'], 'missing base_code_id for %s' % row['id']
                assert row['ref_base_code_id:id'] == row['base_code_id:id']
                add_new_tax_code(row['base_code_id:id'],
                                 'Base - ' + row['name'],
                                 base_code)
            # tax parent
            tax_code = row['TAX_CODE']
            if not tax_code or tax_code == '/':
                tax_code = 'NA'
            if tax_code not in tax_codes:
                raise RuntimeError('undefined tax code %s' % tax_code)
            if tax_code == 'NA':
                if row['amount'] and not row['child_depend']:
                    raise RuntimeError('TAX_CODE not specified '
                                       'for non-zero tax %s' % row['id'])
                if row['tax_code_id:id']:
                    raise RuntimeError('tax_code_id specified '
                                       'for tax %s' % row['id'])
            else:
                if row['child_depend']:
                    raise RuntimeError('TAX_CODE specified '
                                       'with child_depend for %s' % row['id'])
                if not row['amount']:
                    raise RuntimeError('TAX_CODE specified '
                                       'for zero tax %s' % row['id'])
                if not row['tax_code_id:id']:
                    raise RuntimeError('tax_code_id not specified '
                                       'for tax %s' % row['id'])
            if not row['child_depend'] and row['amount']:
                # ... in lux, we have the same code for invoice and refund
                assert row['tax_code_id:id'], 'missing tax_code_id for %s' % row['id']
                assert row['ref_tax_code_id:id'] == row['tax_code_id:id']
                add_new_tax_code(row['tax_code_id:id'],
                                 'Taxe - ' + row['name'],
                                 tax_code)

        for tax_code_id in sorted(new_tax_codes):
            name, parent_code = new_tax_codes[tax_code_id]
            writer.writerow([
                tax_code_id,
                u'lu_tct_m' + parent_code,
                tax_code_id.replace('lu_tax_code_template_', u''),
                u'1',
                u'',
                pycompat.to_text(name),
                u''
            ])

    def taxes_to_csv(self):
        writer = pycompat.csv_writer(open('account.tax.template-%s.csv' %
                                     self.suffix, 'wb'))
        taxes_iterator = self.iter_taxes()
        keys = next(taxes_iterator)
        writer.writerow(keys[3:] + ['sequence'])
        seq = 100
        for row in sorted(taxes_iterator, key=lambda r: r['description']):
            if not _is_true(row['active']):
                continue
            seq += 1
            if row['parent_id:id']:
                cur_seq = seq + 1000
            else:
                cur_seq = seq
            writer.writerow([
                pycompat.to_text(v)
                for v in list(row.values())[3:]
            ] + [cur_seq])

    def fiscal_pos_map_to_csv(self):
        writer = pycompat.csv_writer(open('account.fiscal.'
                                     'position.tax.template-%s.csv' %
                                     self.suffix, 'wb'))
        fiscal_pos_map_iterator = self.iter_fiscal_pos_map()
        keys = next(fiscal_pos_map_iterator)
        writer.writerow(keys)
        for row in fiscal_pos_map_iterator:
            writer.writerow([pycompat.to_text(s) for s in row.values()])


if __name__ == '__main__':
    o = LuxTaxGenerator('tax.xls')
    o.tax_codes_to_csv()
    o.taxes_to_csv()
    o.fiscal_pos_map_to_csv()