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.

414 lines
16 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. # Copyright (C) 2018 - TODAY, Pavlov Media
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. from odoo import api, fields, models, _
  4. class Agreement(models.Model):
  5. _name = 'agreement'
  6. _inherit = ['mail.thread']
  7. def _default_stage_id(self):
  8. return self.env.ref('agreement.agreement_stage_new')
  9. # General
  10. name = fields.Char(string="Title", required=True)
  11. is_template = fields.Boolean(
  12. string="Is a Template?",
  13. default=False,
  14. copy=False,
  15. help="Make this agreement a template.")
  16. version = fields.Integer(
  17. string="Version",
  18. default=1,
  19. copy=False,
  20. help="The versions are used to keep track of document history and "
  21. "previous versions can be referenced.")
  22. revision = fields.Integer(
  23. string="Revision",
  24. default=0,
  25. copy=False,
  26. help="The revision will increase with every save event.")
  27. description = fields.Text(
  28. string="Description",
  29. track_visibility='onchange',
  30. help="Description of the agreement")
  31. dynamic_description = fields.Text(
  32. compute="_compute_dynamic_description",
  33. string="Dynamic Description",
  34. help='Compute dynamic description')
  35. start_date = fields.Date(
  36. string="Start Date",
  37. track_visibility='onchange',
  38. help="When the agreement starts.")
  39. end_date = fields.Date(
  40. string="End Date",
  41. track_visibility='onchange',
  42. help="When the agreement ends.")
  43. color = fields.Integer()
  44. active = fields.Boolean(
  45. string="Active",
  46. default=True,
  47. help="If unchecked, it will allow you to hide the agreement without "
  48. "removing it.")
  49. company_signed_date = fields.Date(
  50. string="Signed on",
  51. track_visibility='onchange',
  52. help="Date the contract was signed by Company.")
  53. partner_signed_date = fields.Date(
  54. string="Signed on",
  55. track_visibility='onchange',
  56. help="Date the contract was signed by the Partner.")
  57. term = fields.Integer(
  58. string="Term (Months)",
  59. track_visibility='onchange',
  60. help="Number of months this agreement/contract is in effect with the "
  61. "partner.")
  62. expiration_notice = fields.Integer(
  63. string="Exp. Notice (Days)",
  64. track_visibility='onchange',
  65. help="Number of Days before expiration to be notified.")
  66. change_notice = fields.Integer(
  67. string="Change Notice (Days)",
  68. track_visibility='onchange',
  69. help="Number of Days to be notified before changes.")
  70. special_terms = fields.Text(
  71. string="Special Terms",
  72. track_visibility='onchange',
  73. help="Any terms that you have agreed to and want to track on the "
  74. "agreement/contract.")
  75. dynamic_special_terms = fields.Text(
  76. compute="_compute_dynamic_special_terms",
  77. string="Dynamic Special Terms",
  78. help='Compute dynamic special terms')
  79. contract_value = fields.Monetary(
  80. compute='_compute_contract_value',
  81. string="Contract Value",
  82. help="Total value of the contract over ther entire term.",
  83. store=True)
  84. reference = fields.Char(
  85. string="Reference",
  86. copy=False,
  87. required=True,
  88. default=lambda self: _('New'),
  89. track_visibility='onchange',
  90. help="ID used for internal contract tracking.")
  91. total_company_mrc = fields.Monetary(
  92. 'Company MRC',
  93. currency_field='currency_id',
  94. help="Total company monthly recurring costs.")
  95. total_customer_mrc = fields.Monetary(
  96. 'Customer MRC',
  97. currency_field='currency_id',
  98. help="Total custemer monthly recurring costs.")
  99. total_company_nrc = fields.Monetary(
  100. 'Company NRC',
  101. currency_field='currency_id',
  102. help="Total company non-recurring costs.")
  103. total_customer_nrc = fields.Monetary(
  104. 'Customer NRC',
  105. currency_field='currency_id',
  106. help="Total custemer non-monthly recurring costs.")
  107. increase_type_id = fields.Many2one(
  108. 'agreement.increasetype',
  109. string="Increase Type",
  110. track_visibility='onchange',
  111. help="The amount that certain rates may increase.")
  112. termination_requested = fields.Date(
  113. string="Termination Requested Date",
  114. track_visibility='onchange',
  115. help="Date that a request for termination was received.")
  116. termination_date = fields.Date(
  117. string="Termination Date",
  118. track_visibility='onchange',
  119. help="Date that the contract was terminated.")
  120. reviewed_date = fields.Date(
  121. string="Reviewed Date",
  122. track_visibility='onchange')
  123. reviewed_user_id = fields.Many2one(
  124. 'res.users',
  125. string="Reviewed By",
  126. track_visibility='onchange')
  127. approved_date = fields.Date(
  128. string="Approved Date",
  129. track_visibility='onchange')
  130. approved_user_id = fields.Many2one(
  131. 'res.users',
  132. string="Approved By",
  133. track_visibility='onchange')
  134. currency_id = fields.Many2one(
  135. 'res.currency',
  136. string='Currency')
  137. partner_id = fields.Many2one(
  138. 'res.partner',
  139. string="Partner",
  140. copy=True,
  141. help="The customer or vendor this agreement is related to.")
  142. company_partner_id = fields.Many2one(
  143. 'res.partner',
  144. string="Company",
  145. copy=True,
  146. default=lambda self: self.env.user.company_id.partner_id)
  147. partner_contact_id = fields.Many2one(
  148. 'res.partner',
  149. string="Partner Contact",
  150. copy=True,
  151. help="The primary partner contact (If Applicable).")
  152. partner_contact_phone = fields.Char(
  153. related='partner_contact_id.phone',
  154. string="Phone")
  155. partner_contact_email = fields.Char(
  156. related='partner_contact_id.email',
  157. string="Email")
  158. company_contact_id = fields.Many2one(
  159. 'res.partner',
  160. string="Company Contact",
  161. copy=True,
  162. help="The primary contact in the company.")
  163. company_contact_phone = fields.Char(
  164. related='company_contact_id.phone',
  165. string="Phone")
  166. company_contact_email = fields.Char(
  167. related='company_contact_id.email',
  168. string="Email")
  169. agreement_type_id = fields.Many2one(
  170. 'agreement.type',
  171. string="Agreement Type",
  172. track_visibility='onchange',
  173. help="Select the type of agreement.")
  174. agreement_subtype_id = fields.Many2one(
  175. 'agreement.subtype',
  176. string="Agreement Sub-type",
  177. track_visibility='onchange',
  178. help="Select the sub-type of this agreement. Sub-Types are related to "
  179. "agreement types.")
  180. product_ids = fields.Many2many(
  181. 'product.template',
  182. string="Products & Services")
  183. sale_order_id = fields.Many2one(
  184. 'sale.order',
  185. string="Sales Order",
  186. track_visibility='onchange',
  187. copy=False,
  188. help="Select the Sales Order that this agreement is related to.")
  189. payment_term_id = fields.Many2one(
  190. 'account.payment.term',
  191. string="Payment Term",
  192. track_visibility='onchange',
  193. help="Terms of payments.")
  194. assigned_user_id = fields.Many2one(
  195. 'res.users',
  196. string="Assigned To",
  197. track_visibility='onchange',
  198. help="Select the user who manages this agreement.")
  199. company_signed_user_id = fields.Many2one(
  200. 'res.users',
  201. string="Signed By",
  202. track_visibility='onchange',
  203. help="The user at our company who authorized/signed the agreement or "
  204. "contract.")
  205. partner_signed_user_id = fields.Many2one(
  206. 'res.partner',
  207. string="Signed By",
  208. track_visibility='onchange',
  209. help="Contact on the account that signed the agreement/contract.")
  210. parent_agreement_id = fields.Many2one(
  211. 'agreement',
  212. string="Parent Agreement",
  213. help="Link this agreement to a parent agreement. For example if this "
  214. "agreement is an amendment to another agreement. This list will "
  215. "only show other agreements related to the same account.")
  216. renewal_type_id = fields.Many2one(
  217. 'agreement.renewaltype',
  218. string="Renewal Type",
  219. track_visibility='onchange',
  220. help="Describes what happens after the contract expires.")
  221. order_lines_services_ids = fields.One2many(
  222. related='sale_order_id.order_line',
  223. string="Service Order Lines",
  224. copy=False)
  225. recital_ids = fields.One2many('agreement.recital', 'agreement_id',
  226. string="Recitals", copy=True)
  227. sections_ids = fields.One2many('agreement.section', 'agreement_id',
  228. string="Sections", copy=True)
  229. clauses_ids = fields.One2many('agreement.clause', 'agreement_id',
  230. string="Clauses", copy=True)
  231. appendix_ids = fields.One2many('agreement.appendix', 'agreement_id',
  232. string="Appendices", copy=True)
  233. serviceprofile_ids = fields.One2many('agreement.serviceprofile',
  234. 'agreement_id',
  235. string="Service Profiles")
  236. analytic_id = fields.Many2one('account.analytic.account',
  237. string='Analytic Account', index=True)
  238. analytic_line_ids = fields.One2many('account.analytic.line',
  239. 'agreement_id',
  240. string='Revenues and Costs',
  241. copy=False)
  242. previous_version_agreements_ids = fields.One2many(
  243. 'agreement',
  244. 'parent_agreement_id',
  245. string="Child Agreements",
  246. copy=False,
  247. domain=[('active', '=', False)])
  248. child_agreements_ids = fields.One2many(
  249. 'agreement',
  250. 'parent_agreement_id',
  251. string="Child Agreements",
  252. copy=False,
  253. domain=[('active', '=', True)])
  254. line_ids = fields.One2many('agreement.line', 'agreement_id',
  255. string="Products/Services", copy=False)
  256. state = fields.Selection([
  257. ('draft', 'Draft'),
  258. ('active', 'Active'),
  259. ('inactive', 'Inactive')],
  260. default='draft',
  261. track_visibility='always')
  262. notification_address_id = fields.Many2one(
  263. 'res.partner',
  264. string="Notification Address",
  265. help="The address to send notificaitons to, if different from "
  266. "customer address.(Address Type = Other)")
  267. signed_contract_filename = fields.Char(
  268. string="Filename")
  269. signed_contract = fields.Binary(
  270. string="Signed Document",
  271. track_visibility='always')
  272. field_id = fields.Many2one(
  273. 'ir.model.fields', string="Field",
  274. help="""Select target field from the related document model. If it is a
  275. relationship field you will be able to select a target field at the
  276. destination of the relationship.""")
  277. sub_object_id = fields.Many2one(
  278. 'ir.model', string="Sub-model",
  279. help="""When a relationship field is selected as first field, this
  280. field shows the document model the relationship goes to.""")
  281. sub_model_object_field_id = fields.Many2one(
  282. 'ir.model.fields', string="Sub-field",
  283. help="""When a relationship field is selected as first field, this
  284. field lets you select the target field within the destination document
  285. model (sub-model).""")
  286. default_value = fields.Char(
  287. string="Default Value",
  288. help="Optional value to use if the target field is empty.")
  289. copyvalue = fields.Char(
  290. string="Placeholder Expression",
  291. help="""Final placeholder expression, to be copy-pasted in the desired
  292. template field.""")
  293. # compute the dynamic content for mako expression
  294. @api.multi
  295. def _compute_dynamic_description(self):
  296. MailTemplates = self.env['mail.template']
  297. for agreement in self:
  298. lang = agreement.partner_id.lang or 'en_US'
  299. description = MailTemplates.with_context(
  300. lang=lang).render_template(
  301. agreement.description, 'agreement', agreement.id)
  302. agreement.dynamic_description = description
  303. @api.multi
  304. def _compute_dynamic_special_terms(self):
  305. MailTemplates = self.env['mail.template']
  306. for agreement in self:
  307. lang = agreement.partner_id.lang or 'en_US'
  308. special_terms = MailTemplates.with_context(
  309. lang=lang).render_template(
  310. agreement.special_terms, 'agreement', agreement.id)
  311. agreement.dynamic_special_terms = special_terms
  312. # compute contract_value field
  313. @api.depends('total_customer_mrc', 'total_customer_nrc', 'term')
  314. def _compute_contract_value(self):
  315. for record in self:
  316. record.contract_value = \
  317. (record.total_customer_mrc * record.term) + \
  318. record.total_customer_nrc
  319. # compute total_company_mrc field
  320. @api.depends('order_lines_services_ids', 'sale_order_id')
  321. def _compute_company_mrc(self):
  322. order_lines = self.env['sale.order.line'].search(
  323. [('is_service', '=', True)])
  324. amount_total = sum(order_lines.mapped('purchase_price'))
  325. for record in self:
  326. record.total_company_mrc = amount_total
  327. @api.onchange('field_id', 'sub_model_object_field_id', 'default_value')
  328. def onchange_copyvalue(self):
  329. self.sub_object_id = False
  330. self.copyvalue = False
  331. self.sub_object_id = False
  332. if self.field_id and not self.field_id.relation:
  333. self.copyvalue = "${object.%s or %s}" % \
  334. (self.field_id.name,
  335. self.default_value or '\'\'')
  336. self.sub_model_object_field_id = False
  337. if self.field_id and self.field_id.relation:
  338. self.sub_object_id = self.env['ir.model'].search(
  339. [('model', '=', self.field_id.relation)])[0]
  340. if self.sub_model_object_field_id:
  341. self.copyvalue = "${object.%s.%s or %s}" %\
  342. (self.field_id.name,
  343. self.sub_model_object_field_id.name,
  344. self.default_value or '\'\'')
  345. # Used for Kanban grouped_by view
  346. @api.model
  347. def _read_group_stage_ids(self, stages, domain, order):
  348. stage_ids = self.env['agreement.stage'].search([])
  349. return stage_ids
  350. stage_id = fields.Many2one(
  351. 'agreement.stage',
  352. string="Stage",
  353. group_expand='_read_group_stage_ids',
  354. help="Select the current stage of the agreement.",
  355. track_visibility='onchange',
  356. index=True,
  357. default=lambda self: self._default_stage_id(),
  358. )
  359. # Create New Version Button
  360. @api.multi
  361. def create_new_version(self, vals):
  362. for rec in self:
  363. if not rec.state == 'draft':
  364. # Make sure status is draft
  365. rec.state = 'draft'
  366. default_vals = {'name': '{0} - OLD VERSION'.format(rec.name),
  367. 'active': False,
  368. 'parent_agreement_id': rec.id}
  369. # Make a current copy and mark it as old
  370. rec.copy(default=default_vals)
  371. # Increment the Version
  372. rec.version = rec.version + 1
  373. # Reset revision to 0 since it's a new version
  374. vals['revision'] = 0
  375. return super(Agreement, self).write(vals)
  376. def create_new_agreement(self):
  377. default_vals = {'name': 'NEW',
  378. 'active': True,
  379. 'version': 1,
  380. 'revision': 0,
  381. 'state': 'draft'}
  382. res = self.copy(default=default_vals)
  383. return {'res_model': 'agreement',
  384. 'type': 'ir.actions.act_window',
  385. 'view_mode': 'form',
  386. 'view_type': 'form',
  387. 'res_id': res.id}
  388. @api.model
  389. def create(self, vals):
  390. if vals.get('reference', _('New')) == _('New'):
  391. vals['reference'] = \
  392. self.env['ir.sequence'].next_by_code('agreement') or _('New')
  393. return super(Agreement, self).create(vals)
  394. # Increments the revision on each save action
  395. @api.multi
  396. def write(self, vals):
  397. vals['revision'] = self.revision + 1
  398. return super(Agreement, self).write(vals)