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.

197 lines
7.4 KiB

  1. # Copyright 2017 LasLabs Inc.
  2. # Copyright 2017 ACSONE SA/NV.
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. from dateutil.relativedelta import relativedelta
  5. from odoo import api, fields, models, _
  6. from odoo.exceptions import ValidationError
  7. class SaleOrderLine(models.Model):
  8. _inherit = 'sale.order.line'
  9. is_contract = fields.Boolean(
  10. string='Is a contract', related="product_id.is_contract"
  11. )
  12. contract_id = fields.Many2one(
  13. comodel_name='account.analytic.account', string='Contract', copy=False
  14. )
  15. contract_template_id = fields.Many2one(
  16. comodel_name='account.analytic.contract',
  17. string='Contract Template',
  18. related='product_id.product_tmpl_id.contract_template_id',
  19. readonly=True,
  20. )
  21. recurring_rule_type = fields.Selection(
  22. [
  23. ('daily', 'Day(s)'),
  24. ('weekly', 'Week(s)'),
  25. ('monthly', 'Month(s)'),
  26. ('monthlylastday', 'Month(s) last day'),
  27. ('yearly', 'Year(s)'),
  28. ],
  29. default='monthly',
  30. string='Recurrence',
  31. help="Specify Interval for automatic invoice generation.",
  32. copy=False,
  33. )
  34. recurring_invoicing_type = fields.Selection(
  35. [('pre-paid', 'Pre-paid'), ('post-paid', 'Post-paid')],
  36. default='pre-paid',
  37. string='Invoicing type',
  38. help="Specify if process date is 'from' or 'to' invoicing date",
  39. copy=False,
  40. )
  41. recurring_interval = fields.Integer(
  42. default=1,
  43. string='Repeat Every',
  44. help="Repeat every (Days/Week/Month/Year)",
  45. copy=False,
  46. )
  47. date_start = fields.Date(string='Date Start')
  48. date_end = fields.Date(string='Date End')
  49. contract_line_id = fields.Many2one(
  50. comodel_name="account.analytic.invoice.line",
  51. string="Contract Line to replace",
  52. required=False,
  53. copy=False,
  54. )
  55. is_auto_renew = fields.Boolean(
  56. string="Auto Renew", related="product_id.is_auto_renew", readonly=True
  57. )
  58. @api.onchange('product_id')
  59. def onchange_product(self):
  60. contract_line_env = self.env['account.analytic.invoice.line']
  61. for rec in self:
  62. if rec.product_id.is_contract:
  63. rec.recurring_rule_type = rec.product_id.recurring_rule_type
  64. rec.recurring_invoicing_type = (
  65. rec.product_id.recurring_invoicing_type
  66. )
  67. rec.recurring_interval = rec.product_id.recurring_interval
  68. rec.date_start = rec.date_start or fields.Date.today()
  69. if rec.product_id.is_auto_renew:
  70. rec.date_end = (
  71. rec.date_start
  72. + contract_line_env.get_relative_delta(
  73. rec.product_id.auto_renew_rule_type,
  74. rec.product_id.auto_renew_interval,
  75. )
  76. - relativedelta(days=1)
  77. )
  78. @api.onchange('date_start')
  79. def onchange_date_start(self):
  80. for rec in self:
  81. if rec.product_id.is_auto_renew:
  82. if not rec.date_start:
  83. rec.date_end = False
  84. else:
  85. rec.date_end = (
  86. rec.date_start
  87. + self.env[
  88. 'account.analytic.invoice.line'
  89. ].get_relative_delta(
  90. rec.product_id.auto_renew_rule_type,
  91. rec.product_id.auto_renew_interval,
  92. )
  93. - relativedelta(days=1)
  94. )
  95. @api.multi
  96. def _prepare_contract_line_values(self, contract):
  97. self.ensure_one()
  98. recurring_next_date = self.env[
  99. 'account.analytic.invoice.line'
  100. ]._compute_first_recurring_next_date(
  101. self.date_start or fields.Date.today(),
  102. self.recurring_invoicing_type,
  103. self.recurring_rule_type,
  104. self.recurring_interval,
  105. )
  106. termination_notice_interval = (
  107. self.product_id.termination_notice_interval
  108. )
  109. termination_notice_rule_type = (
  110. self.product_id.termination_notice_rule_type
  111. )
  112. return {
  113. 'sequence': self.sequence,
  114. 'product_id': self.product_id.id,
  115. 'name': self.name,
  116. 'quantity': self.product_uom_qty,
  117. 'uom_id': self.product_uom.id,
  118. 'price_unit': self.price_unit,
  119. 'discount': self.discount,
  120. 'date_end': self.date_end,
  121. 'date_start': self.date_start or fields.Date.today(),
  122. 'recurring_next_date': recurring_next_date,
  123. 'recurring_interval': self.recurring_interval,
  124. 'recurring_invoicing_type': self.recurring_invoicing_type,
  125. 'recurring_rule_type': self.recurring_rule_type,
  126. 'is_auto_renew': self.product_id.is_auto_renew,
  127. 'auto_renew_interval': self.product_id.auto_renew_interval,
  128. 'auto_renew_rule_type': self.product_id.auto_renew_rule_type,
  129. 'termination_notice_interval': termination_notice_interval,
  130. 'termination_notice_rule_type': termination_notice_rule_type,
  131. 'contract_id': contract.id,
  132. 'sale_order_line_id': self.id,
  133. }
  134. @api.multi
  135. def create_contract_line(self, contract):
  136. contract_line_env = self.env['account.analytic.invoice.line']
  137. contract_line = self.env['account.analytic.invoice.line']
  138. for rec in self:
  139. new_contract_line = contract_line_env.create(
  140. rec._prepare_contract_line_values(contract)
  141. )
  142. contract_line |= new_contract_line
  143. if rec.contract_line_id:
  144. rec.contract_line_id.stop(
  145. rec.date_start - relativedelta(days=1)
  146. )
  147. rec.contract_line_id.successor_contract_line_id = (
  148. new_contract_line
  149. )
  150. new_contract_line.predecessor_contract_line_id = (
  151. self.contract_line_id.id
  152. )
  153. return contract_line
  154. @api.constrains('contract_id')
  155. def _check_contract_sale_partner(self):
  156. for rec in self:
  157. if rec.contract_id:
  158. if rec.order_id.partner_id != rec.contract_id.partner_id:
  159. raise ValidationError(
  160. _(
  161. "Sale Order and contract should be "
  162. "linked to the same partner"
  163. )
  164. )
  165. @api.constrains('product_id', 'contract_id')
  166. def _check_contract_sale_contract_template(self):
  167. for rec in self:
  168. if rec.contract_id:
  169. if (
  170. rec.contract_template_id
  171. != rec.contract_id.contract_template_id
  172. ):
  173. raise ValidationError(
  174. _("Contract product has different contract template")
  175. )
  176. def _compute_invoice_status(self):
  177. super(SaleOrderLine, self)._compute_invoice_status()
  178. for line in self.filtered('contract_id'):
  179. line.invoice_status = 'no'
  180. @api.multi
  181. def invoice_line_create(self, invoice_id, qty):
  182. return super(
  183. SaleOrderLine, self.filtered(lambda l: not l.contract_id)
  184. ).invoice_line_create(invoice_id, qty)