tax2csv.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. from collections import OrderedDict
  2. import xlrd
  3. from odoo.tools import pycompat
  4. def _is_true(s):
  5. return s not in ('F', 'False', 0, '', None, False)
  6. class LuxTaxGenerator:
  7. def __init__(self, filename):
  8. self.workbook = xlrd.open_workbook('tax.xls')
  9. self.sheet_info = \
  10. self.workbook.sheet_by_name('INFO')
  11. self.sheet_taxes = \
  12. self.workbook.sheet_by_name('TAXES')
  13. self.sheet_tax_codes = \
  14. self.workbook.sheet_by_name('TAX.CODES')
  15. self.sheet_fiscal_pos_map = \
  16. self.workbook.sheet_by_name('FISCAL.POSITION.MAPPINGS')
  17. self.suffix = self.sheet_info.cell_value(4, 2)
  18. def iter_tax_codes(self):
  19. keys = [c.value for c in self.sheet_tax_codes.row(0)]
  20. yield keys
  21. for i in range(1, self.sheet_tax_codes.nrows):
  22. row = (c.value for c in self.sheet_tax_codes.row(i))
  23. d = OrderedDict(zip(keys, row))
  24. d['sign'] = int(d['sign'])
  25. d['sequence'] = int(d['sequence'])
  26. yield d
  27. def iter_taxes(self):
  28. keys = [c.value for c in self.sheet_taxes.row(0)]
  29. yield keys
  30. for i in range(1, self.sheet_taxes.nrows):
  31. row = (c.value for c in self.sheet_taxes.row(i))
  32. yield OrderedDict(zip(keys, row))
  33. def iter_fiscal_pos_map(self):
  34. keys = [c.value for c in self.sheet_fiscal_pos_map.row(0)]
  35. yield keys
  36. for i in range(1, self.sheet_fiscal_pos_map.nrows):
  37. row = (c.value for c in self.sheet_fiscal_pos_map.row(i))
  38. yield OrderedDict(zip(keys, row))
  39. def tax_codes_to_csv(self):
  40. writer = pycompat.csv_writer(open('account.tax.code.template-%s.csv' %
  41. self.suffix, 'wb'))
  42. tax_codes_iterator = self.iter_tax_codes()
  43. keys = next(tax_codes_iterator)
  44. writer.writerow(keys)
  45. # write structure tax codes
  46. tax_codes = {} # code: id
  47. for row in tax_codes_iterator:
  48. tax_code = row['code']
  49. if tax_code in tax_codes:
  50. raise RuntimeError('duplicate tax code %s' % tax_code)
  51. tax_codes[tax_code] = row['id']
  52. writer.writerow([pycompat.to_text(v) for v in row.values()])
  53. # read taxes and add leaf tax codes
  54. new_tax_codes = {} # id: parent_code
  55. def add_new_tax_code(tax_code_id, new_name, new_parent_code):
  56. if not tax_code_id:
  57. return
  58. name, parent_code = new_tax_codes.get(tax_code_id, (None, None))
  59. if parent_code and parent_code != new_parent_code:
  60. raise RuntimeError('tax code "%s" already exist with '
  61. 'parent %s while trying to add it with '
  62. 'parent %s' %
  63. (tax_code_id, parent_code, new_parent_code))
  64. else:
  65. new_tax_codes[tax_code_id] = (new_name, new_parent_code)
  66. taxes_iterator = self.iter_taxes()
  67. next(taxes_iterator)
  68. for row in taxes_iterator:
  69. if not _is_true(row['active']):
  70. continue
  71. if row['child_depend'] and row['amount'] != 1:
  72. raise RuntimeError('amount must be one if child_depend '
  73. 'for %s' % row['id'])
  74. # base parent
  75. base_code = row['BASE_CODE']
  76. if not base_code or base_code == '/':
  77. base_code = 'NA'
  78. if base_code not in tax_codes:
  79. raise RuntimeError('undefined tax code %s' % base_code)
  80. if base_code != 'NA':
  81. if row['child_depend']:
  82. raise RuntimeError('base code specified '
  83. 'with child_depend for %s' % row['id'])
  84. if not row['child_depend']:
  85. # ... in lux, we have the same code for invoice and refund
  86. if base_code != 'NA':
  87. assert row['base_code_id:id'], 'missing base_code_id for %s' % row['id']
  88. assert row['ref_base_code_id:id'] == row['base_code_id:id']
  89. add_new_tax_code(row['base_code_id:id'],
  90. 'Base - ' + row['name'],
  91. base_code)
  92. # tax parent
  93. tax_code = row['TAX_CODE']
  94. if not tax_code or tax_code == '/':
  95. tax_code = 'NA'
  96. if tax_code not in tax_codes:
  97. raise RuntimeError('undefined tax code %s' % tax_code)
  98. if tax_code == 'NA':
  99. if row['amount'] and not row['child_depend']:
  100. raise RuntimeError('TAX_CODE not specified '
  101. 'for non-zero tax %s' % row['id'])
  102. if row['tax_code_id:id']:
  103. raise RuntimeError('tax_code_id specified '
  104. 'for tax %s' % row['id'])
  105. else:
  106. if row['child_depend']:
  107. raise RuntimeError('TAX_CODE specified '
  108. 'with child_depend for %s' % row['id'])
  109. if not row['amount']:
  110. raise RuntimeError('TAX_CODE specified '
  111. 'for zero tax %s' % row['id'])
  112. if not row['tax_code_id:id']:
  113. raise RuntimeError('tax_code_id not specified '
  114. 'for tax %s' % row['id'])
  115. if not row['child_depend'] and row['amount']:
  116. # ... in lux, we have the same code for invoice and refund
  117. assert row['tax_code_id:id'], 'missing tax_code_id for %s' % row['id']
  118. assert row['ref_tax_code_id:id'] == row['tax_code_id:id']
  119. add_new_tax_code(row['tax_code_id:id'],
  120. 'Taxe - ' + row['name'],
  121. tax_code)
  122. for tax_code_id in sorted(new_tax_codes):
  123. name, parent_code = new_tax_codes[tax_code_id]
  124. writer.writerow([
  125. tax_code_id,
  126. u'lu_tct_m' + parent_code,
  127. tax_code_id.replace('lu_tax_code_template_', u''),
  128. u'1',
  129. u'',
  130. pycompat.to_text(name),
  131. u''
  132. ])
  133. def taxes_to_csv(self):
  134. writer = pycompat.csv_writer(open('account.tax.template-%s.csv' %
  135. self.suffix, 'wb'))
  136. taxes_iterator = self.iter_taxes()
  137. keys = next(taxes_iterator)
  138. writer.writerow(keys[3:] + ['sequence'])
  139. seq = 100
  140. for row in sorted(taxes_iterator, key=lambda r: r['description']):
  141. if not _is_true(row['active']):
  142. continue
  143. seq += 1
  144. if row['parent_id:id']:
  145. cur_seq = seq + 1000
  146. else:
  147. cur_seq = seq
  148. writer.writerow([
  149. pycompat.to_text(v)
  150. for v in list(row.values())[3:]
  151. ] + [cur_seq])
  152. def fiscal_pos_map_to_csv(self):
  153. writer = pycompat.csv_writer(open('account.fiscal.'
  154. 'position.tax.template-%s.csv' %
  155. self.suffix, 'wb'))
  156. fiscal_pos_map_iterator = self.iter_fiscal_pos_map()
  157. keys = next(fiscal_pos_map_iterator)
  158. writer.writerow(keys)
  159. for row in fiscal_pos_map_iterator:
  160. writer.writerow([pycompat.to_text(s) for s in row.values()])
  161. if __name__ == '__main__':
  162. o = LuxTaxGenerator('tax.xls')
  163. o.tax_codes_to_csv()
  164. o.taxes_to_csv()
  165. o.fiscal_pos_map_to_csv()