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.

416 lines
14 KiB

  1. # -*- coding: utf-8 -*-
  2. from dateutil.relativedelta import relativedelta
  3. from odoo import models, fields, api
  4. from odoo.osv.expression import OR
  5. SCALES = [
  6. ("1", "National"),
  7. ("2", "Regional"),
  8. ("3", "Departmental"),
  9. ("4", "Local"),
  10. ("5", "Licensee"),
  11. ]
  12. class ResPartner(models.Model):
  13. _inherit = "res.partner"
  14. _rec_names_search = ["display_name", "ref", "partner_code", "email"]
  15. # Partner fields
  16. ffck_network = fields.Boolean(string="FFCK network")
  17. first_membership_date = fields.Date("1st membership")
  18. last_membership_date = fields.Date("Last membership")
  19. last_membership_validity = fields.Date(
  20. string="Validity",
  21. compute="_get_membership_validity",
  22. store=True,
  23. )
  24. # Structure typing
  25. partner_scale = fields.Selection(
  26. selection=SCALES,
  27. string="Scale",
  28. compute="_get_partner_scale",
  29. store=True,
  30. )
  31. ffck_structure_type_id = fields.Many2one(
  32. comodel_name="ffck.structure.type",
  33. string="Structure type",
  34. )
  35. age_range = fields.Selection(
  36. related="age_range_id.age_range",
  37. store=True,
  38. )
  39. ffck_membership_type_id = fields.Many2one(
  40. comodel_name="ffck.membership.type",
  41. string="License type",
  42. )
  43. partner_code = fields.Char(
  44. string="FFCK code",
  45. size=6,
  46. index=True,
  47. )
  48. partner_code_editable = fields.Boolean(
  49. string="FFCK code editable",
  50. default=True,
  51. )
  52. # FFCK
  53. ffck_partner_id = fields.Many2one(
  54. comodel_name="res.partner",
  55. string="FFCK partner",
  56. ondelete="restrict",
  57. compute="_get_ffck_partner",
  58. store=True,
  59. )
  60. ffck_partner_code = fields.Char(
  61. string="FFCK",
  62. default="0",
  63. readonly=True,
  64. )
  65. # CRCK
  66. crck_partner_id = fields.Many2one(
  67. comodel_name="res.partner",
  68. string="CRCK partner",
  69. index=True,
  70. compute="_get_crck_partner",
  71. store=True,
  72. )
  73. crck_partner_code = fields.Char(
  74. related="crck_partner_id.partner_code",
  75. store=True,
  76. )
  77. # CDCK
  78. cdck_partner_id = fields.Many2one(
  79. comodel_name="res.partner",
  80. string="CDCK partner",
  81. index=True,
  82. compute="_get_cdck_partner",
  83. store=True,
  84. )
  85. cdck_partner_code = fields.Char(
  86. related="cdck_partner_id.partner_code",
  87. store=True,
  88. )
  89. local_partner_id = fields.Many2one(
  90. comodel_name="res.partner",
  91. string="Local partner",
  92. index=True,
  93. domain=[("ffck_network", "=", True), ("partner_scale", "=", 4)],
  94. )
  95. local_partner_code = fields.Char(
  96. related="local_partner_id.partner_code",
  97. store=True,
  98. )
  99. crck_partner_ids = fields.One2many(
  100. comodel_name="res.partner",
  101. inverse_name="ffck_partner_id",
  102. domain=[("partner_scale", "=", "2")],
  103. string="Regional Comittees",
  104. )
  105. cdck_partner_ids = fields.One2many(
  106. comodel_name="res.partner",
  107. inverse_name="crck_partner_id",
  108. domain=[("partner_scale", "=", "3")],
  109. string="Departmental Comittees",
  110. )
  111. local_partner_ids = fields.One2many(
  112. comodel_name="res.partner",
  113. inverse_name="cdck_partner_id",
  114. domain=[("partner_scale", "=", "4")],
  115. string="Local structures",
  116. )
  117. # Compute hierarchy for searchpanel TODO : move to standalone module
  118. hierarchy_id = fields.Many2one(
  119. comodel_name="res.partner.hierarchy",
  120. string="Hierarchy element",
  121. compute="_compute_partner_hierarchy",
  122. store=True,
  123. )
  124. @api.depends(
  125. "ffck_network",
  126. "partner_scale",
  127. "partner_code",
  128. "local_partner_id",
  129. "cdck_partner_id",
  130. "crck_partner_id",
  131. "ffck_partner_id",
  132. )
  133. def _compute_partner_hierarchy(self):
  134. for partner in self:
  135. partner.hierarchy_id = (
  136. partner.id
  137. if partner.ffck_network
  138. and int(partner.partner_scale) in range(1, 4)
  139. and partner.partner_code
  140. else partner.local_partner_id.id
  141. or partner.cdck_partner_id.id
  142. or partner.crck_partner_id.id
  143. or partner.ffck_partner_id.id
  144. )
  145. # COMPUTES
  146. @api.depends("last_membership_date", "ffck_membership_type_id")
  147. def _get_membership_validity(self):
  148. to_process = self.filtered("ffck_membership_type_id").filtered(
  149. "last_membership_date"
  150. )
  151. for partner in to_process:
  152. date = partner.last_membership_date
  153. year_end = date.replace(day=31, month=12)
  154. membership = partner.ffck_membership_type_id
  155. dur = membership.duration
  156. unit = membership.unit
  157. if unit == "day":
  158. partner.last_membership_validity = date + relativedelta(days=dur - 1)
  159. elif unit == "month":
  160. partner.last_membership_validity = min(
  161. date + relativedelta(days=-1, months=dur),
  162. year_end,
  163. )
  164. elif unit == "year":
  165. partner.last_membership_validity = year_end
  166. for partner in self - to_process:
  167. partner.last_membership_validity = False
  168. @api.depends(
  169. "is_company",
  170. "ffck_network",
  171. "ffck_structure_type_id",
  172. "ffck_structure_type_id.scale",
  173. )
  174. def _get_partner_scale(self):
  175. ffck_partners = self.filtered("ffck_network")
  176. for partner in ffck_partners:
  177. if not partner.is_company:
  178. partner.partner_scale = "5"
  179. else:
  180. partner.partner_scale = (
  181. partner.ffck_structure_type_id.scale
  182. if partner.ffck_structure_type_id
  183. else False
  184. )
  185. for partner in self - ffck_partners:
  186. partner.partner_scale = False
  187. def lock_partner_code(self):
  188. self.write({"partner_code_editable": False})
  189. @api.depends("ffck_network")
  190. def _get_ffck_partner(self):
  191. ffck = self.env.ref("ffck_commons.res_partner_ffck", raise_if_not_found=False)
  192. main = self.env.ref("base.main_partner")
  193. for partner in self:
  194. if main.ref == "FFCK":
  195. if ffck and ffck.active:
  196. ffck.write({"active": False})
  197. partner.ffck_partner_id = main
  198. else:
  199. partner.ffck_partner_id = ffck
  200. @api.depends(
  201. "state_id",
  202. "ffck_network",
  203. "partner_scale",
  204. "cdck_partner_id",
  205. )
  206. def _get_crck_partner(self):
  207. cdck_ok = self.filtered("cdck_partner_id.crck_partner_id")
  208. for partner in cdck_ok:
  209. partner.crck_partner_id = partner.cdck_partner_id.crck_partner_id
  210. todo = self - cdck_ok
  211. if todo:
  212. states = todo.mapped("state_id")
  213. crck_ok = self.with_context(active_test=False).search(
  214. [
  215. ("ffck_network", "=", True),
  216. ("partner_scale", "=", "2"),
  217. ("state_id", "in", states.ids),
  218. ]
  219. )
  220. states_ok = states & crck_ok.mapped("state_id")
  221. crck_by_state = {crck.state_id: crck for crck in crck_ok}
  222. concerned = todo.filtered(
  223. lambda rp: rp.ffck_network
  224. and int(rp.partner_scale) >= 3
  225. and rp.state_id in states_ok
  226. )
  227. for partner in concerned:
  228. state = partner.state_id
  229. partner.crck_partner_id = crck_by_state[state]
  230. # treat unconcerned
  231. for partner in todo - concerned:
  232. partner.crck_partner_id = False
  233. @api.depends(
  234. "is_company",
  235. "ffck_network",
  236. "partner_code",
  237. "partner_scale",
  238. "local_partner_id",
  239. "local_partner_id",
  240. "local_partner_id.partner_code",
  241. "country_department_id",
  242. "country_department_id.code",
  243. )
  244. def _get_cdck_partner(self):
  245. local_ok = self.filtered("local_partner_id.cdck_partner_id")
  246. for partner in local_ok:
  247. partner.cdck_partner_id = partner.local_partner_id.cdck_partner_id
  248. todo = self - local_ok
  249. if todo:
  250. dept_codes = todo.mapped(
  251. lambda rp: rp.ffck_network
  252. and (
  253. rp.is_company
  254. and rp.partner_code
  255. and len(rp.partner_code) >= 2
  256. and int(rp.partner_scale) >= 4
  257. and rp.partner_code[:2]
  258. or not rp.is_company
  259. and rp.country_department_id
  260. and rp.country_department_id.code
  261. )
  262. )
  263. cdck_ok = self.with_context(active_test=False).search(
  264. [
  265. ("is_company", "=", True),
  266. ("ffck_network", "=", True),
  267. ("partner_scale", "=", "3"),
  268. (
  269. "partner_code",
  270. "in",
  271. ["{}00".format(code) for code in dept_codes],
  272. ),
  273. ]
  274. )
  275. cdck_by_code = {cdck.partner_code[:2]: cdck for cdck in cdck_ok}
  276. cdck_codes = cdck_by_code.keys()
  277. structures = todo.filtered(
  278. lambda rp: rp.is_company
  279. and rp.ffck_network
  280. and rp.partner_code
  281. and len(rp.partner_code) >= 2
  282. and int(rp.partner_scale) == 4
  283. and rp.partner_code[:2] in cdck_codes
  284. )
  285. licensees = todo.filtered(
  286. lambda rp: not rp.is_company
  287. and rp.ffck_network
  288. and int(rp.partner_scale) == 5
  289. and rp.country_department_id.code in cdck_codes
  290. )
  291. # Treat structures
  292. for partner in structures:
  293. partner.cdck_partner_id = cdck_by_code[partner.partner_code[:2]]
  294. # Treat licensees
  295. for partner in licensees:
  296. dept = partner.country_department_id.code
  297. if dept in cdck_codes:
  298. partner.cdck_partner_id = cdck_by_code[dept]
  299. # treat others
  300. for partner in todo - (structures | licensees):
  301. partner.cdck_partner_id = False
  302. @api.depends(
  303. "ffck_network",
  304. "partner_scale",
  305. "ffck_partner_id",
  306. "crck_partner_id",
  307. "cdck_partner_id",
  308. "local_partner_id",
  309. )
  310. def _get_ffck_parent(self):
  311. field_by_scale = {
  312. "2": "ffck_partner_id",
  313. "3": "crck_partner_id",
  314. "4": "cdck_partner_id",
  315. "5": "local_partner_id",
  316. }
  317. for partner in self:
  318. if partner.ffck_network and int(partner.partner_scale) in range(2, 6):
  319. partner.ffck_parent_id = getattr(
  320. partner, field_by_scale[partner.partner_scale]
  321. )
  322. else:
  323. partner.ffck_parent_id = False
  324. # INHERITANCE
  325. # @api.model
  326. # def name_search(self, name="", args=None, operator="ilike", limit=100):
  327. # """Allow searching by sequence code by default."""
  328. # # Do not add any domain when user just clicked on search widget
  329. # if not (name == "" and operator == "ilike"):
  330. # # The dangling | is needed to combine with the domain added by super()
  331. # args = OR(
  332. # [
  333. # [
  334. # "&",
  335. # ("ffck_network", "=", True),
  336. # ("partner_code", "=like", name + "%"),
  337. # ],
  338. # args or [],
  339. # ]
  340. # )
  341. # return super().name_search(name, args, operator, limit)
  342. @api.depends(
  343. "is_company",
  344. "name",
  345. "parent_id.display_name",
  346. "type",
  347. "company_name",
  348. "commercial_company_name",
  349. "ref",
  350. "partner_code",
  351. )
  352. def _compute_display_name(self):
  353. return super()._compute_display_name()
  354. def name_get(self):
  355. res = []
  356. ffck_partners = self.filtered("ffck_network")
  357. others = self - ffck_partners
  358. for record in ffck_partners:
  359. code = record.partner_code
  360. ref = record.ref
  361. res.append(
  362. (
  363. record.id,
  364. "{}{}{}".format(
  365. code and code + " - " or "",
  366. ref and record.is_company and ref + " - " or "",
  367. super(ResPartner, record).name_get()[0][1],
  368. ),
  369. )
  370. )
  371. res += super(ResPartner, others).name_get()
  372. return res
  373. # ONCHANGES
  374. @api.onchange("partner_code", "company_type", "ffck_network")
  375. def onchange_partner_code(self):
  376. if self.ffck_network and not self.is_company:
  377. code = self.partner_code or "000000"
  378. if len(code) < 6:
  379. self.update({"partner_code": code.zfill(6)})
  380. elif len(code) > 6:
  381. self.update({"partner_code": code[:-6]})
  382. # CRUD
  383. # @api.model_create_multi
  384. # def create(self, vals_list):
  385. # for vals in vals_list:
  386. # vals.update({"partner_code_editable": False})
  387. # return super().create(vals_list)
  388. def write(self, vals):
  389. is_company = vals.get("is_company", None)
  390. if is_company is True:
  391. vals.update({"ffck_membership_type_id": False})
  392. if is_company is False:
  393. vals.update({"ffck_structure_type_id": False})
  394. return super().write(vals)