You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
10 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # OpenERP, Open Source Management Solution
  5. # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as
  9. # published by the Free Software Foundation, either version 3 of the
  10. # License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. ##############################################################################
  21. from dateutil.relativedelta import relativedelta
  22. import datetime
  23. import logging
  24. import time
  25. from openerp.osv import osv, fields
  26. from openerp.tools.translate import _
  27. from openerp.addons.decimal_precision import decimal_precision as dp
  28. _logger = logging.getLogger(__name__)
  29. class account_analytic_invoice_line(osv.osv):
  30. _name = "account.analytic.invoice.line"
  31. def _amount_line(
  32. self, cr, uid, ids, prop, unknow_none, unknow_dict, context=None):
  33. res = {}
  34. for line in self.browse(cr, uid, ids, context=context):
  35. res[line.id] = line.quantity * line.price_unit
  36. if line.analytic_account_id.pricelist_id:
  37. cur = line.analytic_account_id.pricelist_id.currency_id
  38. res[line.id] = self.pool.get('res.currency').round(
  39. cr, uid, cur, res[line.id])
  40. return res
  41. _columns = {
  42. 'product_id': fields.many2one(
  43. 'product.product', 'Product', required=True),
  44. 'analytic_account_id': fields.many2one(
  45. 'account.analytic.account', 'Analytic Account'),
  46. 'name': fields.text('Description', required=True),
  47. 'quantity': fields.float('Quantity', required=True),
  48. 'uom_id': fields.many2one(
  49. 'product.uom', 'Unit of Measure', required=True),
  50. 'price_unit': fields.float('Unit Price', required=True),
  51. 'price_subtotal': fields.function(
  52. _amount_line, string='Sub Total',
  53. type="float", digits_compute=dp.get_precision('Account')),
  54. }
  55. _defaults = {
  56. 'quantity': 1,
  57. }
  58. def product_id_change(
  59. self, cr, uid, ids, product, uom_id, qty=0, name='',
  60. partner_id=False, price_unit=False, pricelist_id=False,
  61. company_id=None, context=None):
  62. context = context or {}
  63. uom_obj = self.pool.get('product.uom')
  64. company_id = company_id or False
  65. context.update(
  66. {'company_id': company_id,
  67. 'force_company': company_id,
  68. 'pricelist_id': pricelist_id})
  69. if not product:
  70. return {
  71. 'value': {'price_unit': 0.0},
  72. 'domain': {'product_uom': []}}
  73. if partner_id:
  74. part = self.pool.get('res.partner').browse(
  75. cr, uid, partner_id, context=context)
  76. if part.lang:
  77. context.update({'lang': part.lang})
  78. result = {}
  79. res = self.pool.get('product.product').browse(
  80. cr, uid, product, context=context)
  81. result.update(
  82. {'name': res.partner_ref or False,
  83. 'uom_id': uom_id or res.uom_id.id or False,
  84. 'price_unit': res.list_price or 0.0})
  85. if res.description:
  86. result['name'] += '\n'+res.description
  87. res_final = {'value': result}
  88. if result['uom_id'] != res.uom_id.id:
  89. new_price = uom_obj._compute_price(
  90. cr, uid, res.uom_id.id,
  91. res_final['value']['price_unit'], result['uom_id'])
  92. res_final['value']['price_unit'] = new_price
  93. return res_final
  94. class account_analytic_account(osv.osv):
  95. _name = "account.analytic.account"
  96. _inherit = "account.analytic.account"
  97. _columns = {
  98. 'recurring_invoice_line_ids': fields.one2many(
  99. 'account.analytic.invoice.line', 'analytic_account_id',
  100. 'Invoice Lines'),
  101. 'recurring_invoices': fields.boolean(
  102. 'Generate recurring invoices automatically'),
  103. 'recurring_rule_type': fields.selection(
  104. [('daily', 'Day(s)'),
  105. ('weekly', 'Week(s)'),
  106. ('monthly', 'Month(s)'),
  107. ('yearly', 'Year(s)'),
  108. ], 'Recurrency',
  109. help="Invoice automatically repeat at specified interval"),
  110. 'recurring_interval': fields.integer(
  111. 'Repeat Every', help="Repeat every (Days/Week/Month/Year)"),
  112. 'recurring_next_date': fields.date('Date of Next Invoice'),
  113. }
  114. _defaults = {
  115. 'recurring_interval': 1,
  116. 'recurring_next_date': lambda *a: time.strftime('%Y-%m-%d'),
  117. 'recurring_rule_type': 'monthly'
  118. }
  119. def onchange_recurring_invoices(
  120. self, cr, uid, ids, recurring_invoices,
  121. date_start=False, context=None):
  122. value = {}
  123. if date_start and recurring_invoices:
  124. value = {'value': {'recurring_next_date': date_start}}
  125. return value
  126. def _prepare_invoice(self, cr, uid, contract, context=None):
  127. context = context or {}
  128. inv_obj = self.pool.get('account.invoice')
  129. journal_obj = self.pool.get('account.journal')
  130. fpos_obj = self.pool.get('account.fiscal.position')
  131. lang_obj = self.pool.get('res.lang')
  132. if not contract.partner_id:
  133. raise osv.except_osv(
  134. _('No Customer Defined!'),
  135. _("You must first select a Customer for Contract %s!") %
  136. contract.name)
  137. fpos = contract.partner_id.property_account_position or False
  138. journal_ids = journal_obj.search(
  139. cr, uid,
  140. [('type', '=', 'sale'),
  141. ('company_id', '=', contract.company_id.id or False)],
  142. limit=1)
  143. if not journal_ids:
  144. raise osv.except_osv(
  145. _('Error!'),
  146. _('Please define a sale journal for the company "%s".') %
  147. (contract.company_id.name or '', ))
  148. partner_payment_term = contract.partner_id.property_payment_term.id
  149. inv_data = {
  150. 'reference': contract.code or False,
  151. 'account_id': contract.partner_id.property_account_receivable.id,
  152. 'type': 'out_invoice',
  153. 'partner_id': contract.partner_id.id,
  154. 'currency_id': contract.partner_id.property_product_pricelist.id,
  155. 'journal_id': len(journal_ids) and journal_ids[0] or False,
  156. 'date_invoice': contract.recurring_next_date,
  157. 'origin': contract.name,
  158. 'fiscal_position': fpos and fpos.id,
  159. 'payment_term': partner_payment_term,
  160. 'company_id': contract.company_id.id or False,
  161. }
  162. invoice_id = inv_obj.create(cr, uid, inv_data, context=context)
  163. for line in contract.recurring_invoice_line_ids:
  164. res = line.product_id
  165. account_id = res.property_account_income.id
  166. if not account_id:
  167. account_id = res.categ_id.property_account_income_categ.id
  168. account_id = fpos_obj.map_account(cr, uid, fpos, account_id)
  169. taxes = res.taxes_id or False
  170. tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
  171. if 'old_date' in context:
  172. lang_ids = lang_obj.search(
  173. cr, uid, [('code', '=', contract.partner_id.lang)],
  174. context=context)
  175. format = lang_obj.browse(
  176. cr, uid, lang_ids, context=context)[0].date_format
  177. line.name = line.name.replace(
  178. '#START#', context['old_date'].strftime(format))
  179. line.name = line.name.replace(
  180. '#END#', context['next_date'].strftime(format))
  181. invoice_line_vals = {
  182. 'name': line.name,
  183. 'account_id': account_id,
  184. 'account_analytic_id': contract.id,
  185. 'price_unit': line.price_unit or 0.0,
  186. 'quantity': line.quantity,
  187. 'uos_id': line.uom_id.id or False,
  188. 'product_id': line.product_id.id or False,
  189. 'invoice_id': invoice_id,
  190. 'invoice_line_tax_id': [(6, 0, tax_id)],
  191. }
  192. self.pool.get('account.invoice.line').create(
  193. cr, uid, invoice_line_vals, context=context)
  194. inv_obj.button_compute(cr, uid, [invoice_id], context=context)
  195. return invoice_id
  196. def recurring_create_invoice(self, cr, uid, automatic=False, context=None):
  197. context = context or {}
  198. current_date = time.strftime('%Y-%m-%d')
  199. contract_ids = self.search(
  200. cr, uid,
  201. [('recurring_next_date', '<=', current_date),
  202. ('state', '=', 'open'),
  203. ('recurring_invoices', '=', True)])
  204. for contract in self.browse(cr, uid, contract_ids, context=context):
  205. next_date = datetime.datetime.strptime(
  206. contract.recurring_next_date or current_date, "%Y-%m-%d")
  207. interval = contract.recurring_interval
  208. if contract.recurring_rule_type == 'daily':
  209. old_date = next_date-relativedelta(days=+interval)
  210. new_date = next_date+relativedelta(days=+interval)
  211. elif contract.recurring_rule_type == 'weekly':
  212. old_date = next_date-relativedelta(weeks=+interval)
  213. new_date = next_date+relativedelta(weeks=+interval)
  214. else:
  215. old_date = next_date+relativedelta(months=+interval)
  216. new_date = next_date+relativedelta(months=+interval)
  217. context['old_date'] = old_date
  218. context['next_date'] = datetime.datetime.strptime(
  219. contract.recurring_next_date or current_date, "%Y-%m-%d")
  220. self._prepare_invoice(
  221. cr, uid, contract, context=context)
  222. self.write(
  223. cr, uid, [contract.id],
  224. {'recurring_next_date': new_date.strftime('%Y-%m-%d')},
  225. context=context)
  226. return True