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.

358 lines
12 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. # Copyright 2020 Coop IT Easy SCRL fs
  2. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
  3. import logging
  4. import uuid
  5. from datetime import date
  6. from odoo import api, fields, models
  7. from odoo.exceptions import UserError, ValidationError
  8. from odoo.tools.translate import _
  9. _logger = logging.getLogger(__name__)
  10. class ResPartner(models.Model):
  11. _inherit = "res.partner"
  12. profit_margin = fields.Float(string="Product Margin [%]")
  13. @api.multi
  14. @api.constrains("profit_margin")
  15. def _check_margin(self):
  16. for product in self:
  17. if product.profit_margin < 0.0:
  18. raise UserError(_("Percentages for Profit Margin must >= 0."))
  19. class BeesdooProduct(models.Model):
  20. _inherit = "product.template"
  21. eco_label = fields.Many2one(
  22. "beesdoo.product.label", domain=[("type", "=", "eco")]
  23. )
  24. local_label = fields.Many2one(
  25. "beesdoo.product.label", domain=[("type", "=", "local")]
  26. )
  27. fair_label = fields.Many2one(
  28. "beesdoo.product.label", domain=[("type", "=", "fair")]
  29. )
  30. origin_label = fields.Many2one(
  31. "beesdoo.product.label", domain=[("type", "=", "delivery")]
  32. )
  33. main_seller_id = fields.Many2one(
  34. "res.partner",
  35. string="Main Seller",
  36. compute="_compute_main_seller_id",
  37. store=True,
  38. )
  39. display_unit = fields.Many2one("uom.uom")
  40. default_reference_unit = fields.Many2one("uom.uom")
  41. display_weight = fields.Float(
  42. compute="_compute_display_weight", store=True
  43. )
  44. total_with_vat = fields.Float(
  45. compute="_compute_total",
  46. store=True,
  47. string="Total Sales Price with VAT",
  48. )
  49. total_with_vat_by_unit = fields.Float(
  50. compute="_compute_total",
  51. store=True,
  52. string="Total Sales Price with VAT by Reference Unit",
  53. )
  54. total_deposit = fields.Float(
  55. compute="_compute_total", store=True, string="Deposit Price"
  56. )
  57. label_to_be_printed = fields.Boolean("Print label?")
  58. label_last_printed = fields.Datetime("Label last printed on")
  59. note = fields.Text("Comments")
  60. suggested_price = fields.Float(
  61. string="Suggested Price", compute="_compute_cost", readOnly=True,
  62. help="""
  63. This field computes a suggested price based on the 'Product Margin'
  64. field on Partners (Vendors), if it's set, or otherwise on the 'Product
  65. Margin' field in Product Categories (which has a default value).
  66. """
  67. )
  68. deadline_for_sale = fields.Integer(string="Deadline for sale(days)")
  69. deadline_for_consumption = fields.Integer(
  70. string="Deadline for consumption(days)"
  71. )
  72. ingredients = fields.Char(string="Ingredient")
  73. scale_label_info_1 = fields.Char(string="Scale lable info 1")
  74. scale_label_info_2 = fields.Char(string="Scale lable info 2")
  75. scale_sale_unit = fields.Char(
  76. compute="_compute_scale_sale_uom", string="Scale sale unit", store=True
  77. )
  78. scale_category = fields.Many2one(
  79. "beesdoo.scale.category", string="Scale Category"
  80. )
  81. scale_category_code = fields.Integer(
  82. related="scale_category.code",
  83. string="Scale category code",
  84. readonly=True,
  85. store=True,
  86. )
  87. @api.depends("uom_id", "uom_id.category_id", "uom_id.category_id.type")
  88. @api.multi
  89. def _compute_scale_sale_uom(self):
  90. for product in self:
  91. if product.uom_id.category_id.type == "unit":
  92. product.scale_sale_unit = "F"
  93. elif product.uom_id.category_id.type == "weight":
  94. product.scale_sale_unit = "P"
  95. def _get_main_supplier_info(self):
  96. far_future = date(3000, 1, 1)
  97. def sort_date_first(seller):
  98. if seller.date_start:
  99. return seller.date_start
  100. else:
  101. return far_future
  102. suppliers = self.seller_ids.sorted(key=sort_date_first, reverse=True)
  103. if suppliers:
  104. return suppliers[0]
  105. else:
  106. return suppliers
  107. @api.multi
  108. def generate_barcode(self):
  109. self.ensure_one()
  110. if self.to_weight:
  111. seq_internal_code = self.env.ref(
  112. "beesdoo_product.seq_ean_product_internal_ref"
  113. )
  114. bc = ""
  115. if not self.default_code:
  116. rule = self.env["barcode.rule"].search(
  117. [
  118. (
  119. "name",
  120. "=",
  121. "Price Barcodes (Computed Weight) 2 Decimals",
  122. )
  123. ]
  124. )[0]
  125. default_code = seq_internal_code.next_by_id()
  126. while (
  127. self.search_count([("default_code", "=", default_code)])
  128. > 1
  129. ):
  130. default_code = seq_internal_code.next_by_id()
  131. self.default_code = default_code
  132. ean = "02" + self.default_code[0:5] + "000000"
  133. bc = ean[0:12] + str(
  134. self.env["barcode.nomenclature"].ean_checksum(ean)
  135. )
  136. else:
  137. rule = self.env["barcode.rule"].search(
  138. [("name", "=", "Beescoop Product Barcodes")]
  139. )[0]
  140. size = 13 - len(rule.pattern)
  141. ean = rule.pattern + str(uuid.uuid4().fields[-1])[:size]
  142. bc = ean[0:12] + str(
  143. self.env["barcode.nomenclature"].ean_checksum(ean)
  144. )
  145. # Make sure there is no other active member with the same barcode
  146. while self.search_count([("barcode", "=", bc)]) > 1:
  147. ean = rule.pattern + str(uuid.uuid4().fields[-1])[:size]
  148. bc = ean[0:12] + str(
  149. self.env["barcode.nomenclature"].ean_checksum(ean)
  150. )
  151. _logger.info("barcode :", bc)
  152. self.barcode = bc
  153. @api.multi
  154. @api.depends("seller_ids", "seller_ids.date_start")
  155. def _compute_main_seller_id(self):
  156. for product in self:
  157. # Calcule le vendeur associé qui a la date de début la plus récente
  158. # et plus petite qu’aujourd’hui
  159. sellers_ids = product._get_main_supplier_info()
  160. product.main_seller_id = (
  161. sellers_ids and sellers_ids[0].name or False
  162. )
  163. @api.multi
  164. @api.depends(
  165. "taxes_id",
  166. "list_price",
  167. "taxes_id.amount",
  168. "taxes_id.tax_group_id",
  169. "display_weight",
  170. "weight",
  171. )
  172. def _compute_total(self):
  173. for product in self:
  174. consignes_group = self.env.ref(
  175. "beesdoo_product.consignes_group_tax", raise_if_not_found=False
  176. )
  177. taxes_included = set(product.taxes_id.mapped("price_include"))
  178. if len(taxes_included) == 0:
  179. product.total_with_vat = product.list_price
  180. return True
  181. elif len(taxes_included) > 1:
  182. raise ValidationError(
  183. _("Several tax strategies (price_include) defined for %s")
  184. % product.name
  185. )
  186. elif taxes_included.pop():
  187. product.total_with_vat = product.list_price
  188. product.total_deposit = sum(
  189. [
  190. tax._compute_amount(
  191. product.list_price, product.list_price
  192. )
  193. for tax in product.taxes_id
  194. if tax.tax_group_id == consignes_group
  195. ]
  196. )
  197. else:
  198. tax_amount_sum = sum(
  199. [
  200. tax._compute_amount(
  201. product.list_price, product.list_price
  202. )
  203. for tax in product.taxes_id
  204. if tax.tax_group_id != consignes_group
  205. ]
  206. )
  207. product.total_with_vat = product.list_price + tax_amount_sum
  208. product.total_deposit = sum(
  209. [
  210. tax._compute_amount(product.list_price, product.list_price)
  211. for tax in product.taxes_id
  212. if tax.tax_group_id == consignes_group
  213. ]
  214. )
  215. if product.display_weight > 0:
  216. product.total_with_vat_by_unit = (
  217. product.total_with_vat / product.weight
  218. )
  219. @api.multi
  220. @api.depends("weight", "display_unit")
  221. def _compute_display_weight(self):
  222. for product in self:
  223. product.display_weight = (
  224. product.weight * product.display_unit.factor
  225. )
  226. @api.multi
  227. @api.constrains("display_unit", "default_reference_unit")
  228. def _unit_same_category(self):
  229. for product in self:
  230. if (
  231. product.display_unit.category_id
  232. != product.default_reference_unit.category_id
  233. ):
  234. raise UserError(
  235. _(
  236. "Reference Unit and Display Unit should belong to the "
  237. "same category "
  238. )
  239. )
  240. @api.multi
  241. @api.depends("seller_ids")
  242. def _compute_cost(self):
  243. for product in self:
  244. suppliers = product._get_main_supplier_info()
  245. if len(suppliers) > 0:
  246. price = suppliers[0].price
  247. profit_margin_supplier = suppliers[0].name.profit_margin
  248. profit_margin_product_category = suppliers[
  249. 0
  250. ].product_tmpl_id.categ_id.profit_margin
  251. profit_margin = (
  252. profit_margin_supplier or profit_margin_product_category
  253. )
  254. product.suggested_price = (
  255. price * product.uom_po_id.factor
  256. ) * (1 + profit_margin / 100)
  257. class BeesdooScaleCategory(models.Model):
  258. _name = "beesdoo.scale.category"
  259. _description = "beesdoo.scale.category"
  260. name = fields.Char(string="Scale name category")
  261. code = fields.Integer(string="Category code")
  262. _sql_constraints = [
  263. (
  264. "code_scale_categ_uniq",
  265. "unique (code)",
  266. "The code of the scale category must be unique !",
  267. )
  268. ]
  269. class BeesdooProductLabel(models.Model):
  270. _name = "beesdoo.product.label"
  271. _description = "beesdoo.product.label"
  272. name = fields.Char()
  273. type = fields.Selection(
  274. [
  275. ("eco", "Écologique"),
  276. ("local", "Local"),
  277. ("fair", "Équitable"),
  278. ("delivery", "Distribution"),
  279. ]
  280. )
  281. color_code = fields.Char()
  282. active = fields.Boolean(default=True)
  283. class BeesdooProductCategory(models.Model):
  284. _inherit = "product.category"
  285. profit_margin = fields.Float(default="10.0", string="Product Margin [%]")
  286. @api.multi
  287. @api.constrains("profit_margin")
  288. def _check_margin(self):
  289. for product in self:
  290. if product.profit_margin < 0.0:
  291. raise UserError(_("Percentages for Profit Margin must >= 0."))
  292. class BeesdooProductSupplierInfo(models.Model):
  293. _inherit = "product.supplierinfo"
  294. price = fields.Float("Price")
  295. class BeesdooUOMCateg(models.Model):
  296. _inherit = "uom.category"
  297. type = fields.Selection(
  298. [
  299. ("unit", "Unit"),
  300. ("weight", "Weight"),
  301. ("time", "Time"),
  302. ("distance", "Distance"),
  303. ("surface", "Surface"),
  304. ("volume", "Volume"),
  305. ("other", "Other"),
  306. ],
  307. string="Category type",
  308. default="unit",
  309. )