product_pricelist.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. # -*- encoding: utf-8 -*-
  2. import logging
  3. from itertools import chain
  4. import time
  5. from openerp import tools
  6. from openerp.osv import fields, osv
  7. from openerp.tools.translate import _
  8. from openerp.exceptions import except_orm
  9. _logger = logging.getLogger(__name__)
  10. class product_pricelist(osv.osv):
  11. _inherit = "product.pricelist"
  12. def _price_rule_get_multi(self, cr, uid, pricelist, products_by_qty_by_partner, context=None):
  13. """
  14. Overwrite of the original product pricelist calculation to add curve dependicy on rules.
  15. The method lacks hooks this is why the whole method is copied wich is not a good practice
  16. :param pricelist:
  17. :param products_by_qty_by_partner:
  18. :return:
  19. """
  20. date = context.get('date') or time.strftime('%Y-%m-%d')
  21. date = date[0:10]
  22. products = map(lambda x: x[0], products_by_qty_by_partner)
  23. currency_obj = self.pool.get('res.currency')
  24. product_obj = self.pool.get('product.template')
  25. product_uom_obj = self.pool.get('product.uom')
  26. price_type_obj = self.pool.get('product.price.type')
  27. if not products:
  28. return {}
  29. version = False
  30. for v in pricelist.version_id:
  31. if ((v.date_start is False) or (v.date_start <= date)) and ((v.date_end is False) or (v.date_end >= date)):
  32. version = v
  33. break
  34. if not version:
  35. raise osv.except_osv(_('Warning!'), _("At least one pricelist has no active version !\nPlease create or activate one."))
  36. categ_ids = {}
  37. for p in products:
  38. categ = p.categ_id
  39. while categ:
  40. categ_ids[categ.id] = True
  41. categ = categ.parent_id
  42. categ_ids = categ_ids.keys()
  43. is_product_template = products[0]._name == "product.template"
  44. if is_product_template:
  45. prod_tmpl_ids = [tmpl.id for tmpl in products]
  46. # all variants of all products
  47. prod_ids = [p.id for p in
  48. list(chain.from_iterable([t.product_variant_ids for t in products]))]
  49. else:
  50. prod_ids = [product.id for product in products]
  51. prod_tmpl_ids = [product.product_tmpl_id.id for product in products]
  52. curve_ids = []
  53. for p in products:
  54. if p.product_curve_id:
  55. curve_ids.append(p.product_curve_id.id)
  56. if curve_ids:
  57. # Load all rules
  58. cr.execute(
  59. 'SELECT i.id '
  60. 'FROM product_pricelist_item AS i '
  61. 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = any(%s)) '
  62. 'AND (product_id IS NULL OR (product_id = any(%s))) '
  63. 'AND ((categ_id IS NULL) OR (categ_id = any(%s))) '
  64. 'AND ((product_curve_id IS NULL) OR (product_curve_id = any(%s))) '
  65. 'AND (price_version_id = %s) '
  66. 'ORDER BY sequence, min_quantity desc',
  67. (prod_tmpl_ids, prod_ids, categ_ids, curve_ids, version.id))
  68. else:
  69. # Load all rules
  70. cr.execute(
  71. 'SELECT i.id '
  72. 'FROM product_pricelist_item AS i '
  73. 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = any(%s)) '
  74. 'AND (product_id IS NULL OR (product_id = any(%s))) '
  75. 'AND ((categ_id IS NULL) OR (categ_id = any(%s))) '
  76. 'AND (price_version_id = %s) '
  77. 'ORDER BY sequence, min_quantity desc',
  78. (prod_tmpl_ids, prod_ids, categ_ids, version.id))
  79. item_ids = [x[0] for x in cr.fetchall()]
  80. items = self.pool.get('product.pricelist.item').browse(cr, uid, item_ids, context=context)
  81. price_types = {}
  82. results = {}
  83. for product, qty, partner in products_by_qty_by_partner:
  84. results[product.id] = 0.0
  85. rule_id = False
  86. price = False
  87. # Final unit price is computed according to `qty` in the `qty_uom_id` UoM.
  88. # An intermediary unit price may be computed according to a different UoM, in
  89. # which case the price_uom_id contains that UoM.
  90. # The final price will be converted to match `qty_uom_id`.
  91. qty_uom_id = context.get('uom') or product.uom_id.id
  92. price_uom_id = product.uom_id.id
  93. qty_in_product_uom = qty
  94. if qty_uom_id != product.uom_id.id:
  95. try:
  96. qty_in_product_uom = product_uom_obj._compute_qty(
  97. cr,
  98. uid,
  99. context['uom'],
  100. qty,
  101. product.uom_id.id or product.uos_id.id
  102. )
  103. except except_orm:
  104. # Ignored - incompatible UoM in context, use default product UoM
  105. pass
  106. for rule in items:
  107. if rule.min_quantity and qty_in_product_uom < rule.min_quantity:
  108. continue
  109. if is_product_template:
  110. if rule.product_tmpl_id and product.id != rule.product_tmpl_id.id:
  111. continue
  112. if rule.product_id and \
  113. (product.product_variant_count > 1 or product.product_variant_ids[0].id != rule.product_id.id):
  114. # product rule acceptable on template if has only one variant
  115. continue
  116. else:
  117. if rule.product_tmpl_id and product.product_tmpl_id.id != rule.product_tmpl_id.id:
  118. continue
  119. if rule.product_id and product.id != rule.product_id.id:
  120. continue
  121. if rule.categ_id:
  122. cat = product.categ_id
  123. while cat:
  124. if cat.id == rule.categ_id.id:
  125. break
  126. cat = cat.parent_id
  127. if not cat:
  128. continue
  129. if rule.product_curve_id:
  130. if product.product_curve_id != rule.product_curve_id:
  131. continue
  132. if rule.base == -1:
  133. if rule.base_pricelist_id:
  134. price_tmp = self._price_get_multi(
  135. cr,
  136. uid,
  137. rule.base_pricelist_id,
  138. [
  139. (
  140. product,
  141. qty,
  142. partner
  143. )
  144. ],
  145. context=context
  146. )[product.id]
  147. ptype_src = rule.base_pricelist_id.currency_id.id
  148. price_uom_id = qty_uom_id
  149. price = currency_obj.compute(
  150. cr,
  151. uid,
  152. ptype_src,
  153. pricelist.currency_id.id,
  154. price_tmp,
  155. round=False,
  156. context=context
  157. )
  158. elif rule.base == -2:
  159. seller = False
  160. for seller_id in product.seller_ids:
  161. if (not partner) or (seller_id.name.id != partner):
  162. continue
  163. seller = seller_id
  164. if not seller and product.seller_ids:
  165. seller = product.seller_ids[0]
  166. if seller:
  167. qty_in_seller_uom = qty
  168. seller_uom = seller.product_uom.id
  169. if qty_uom_id != seller_uom:
  170. qty_in_seller_uom = product_uom_obj._compute_qty(
  171. cr,
  172. uid,
  173. qty_uom_id,
  174. qty,
  175. to_uom_id=seller_uom
  176. )
  177. price_uom_id = seller_uom
  178. for line in seller.pricelist_ids:
  179. if line.min_quantity <= qty_in_seller_uom:
  180. price = line.price
  181. else:
  182. if rule.base not in price_types:
  183. price_types[rule.base] = price_type_obj.browse(
  184. cr,
  185. uid,
  186. int(rule.base),
  187. context=context
  188. )
  189. price_type = price_types[rule.base]
  190. # price_get returns the price in the context UoM, i.e. qty_uom_id
  191. price_uom_id = qty_uom_id
  192. price = currency_obj.compute(
  193. cr,
  194. uid,
  195. price_type.currency_id.id,
  196. pricelist.currency_id.id,
  197. product_obj._price_get(
  198. cr,
  199. uid,
  200. [product],
  201. price_type.field,
  202. context=context
  203. )[product.id],
  204. round=False,
  205. context=context
  206. )
  207. if price is not False:
  208. price_limit = price
  209. price = price * (1.0+(rule.price_discount or 0.0))
  210. if rule.price_round:
  211. price = tools.float_round(price, precision_rounding=rule.price_round)
  212. convert_to_price_uom = (lambda price: product_uom_obj._compute_price(
  213. cr, uid, product.uom_id.id,
  214. price, price_uom_id))
  215. if rule.price_surcharge:
  216. price_surcharge = convert_to_price_uom(rule.price_surcharge)
  217. price += price_surcharge
  218. if rule.price_min_margin:
  219. price_min_margin = convert_to_price_uom(rule.price_min_margin)
  220. price = max(price, price_limit + price_min_margin)
  221. if rule.price_max_margin:
  222. price_max_margin = convert_to_price_uom(rule.price_max_margin)
  223. price = min(price, price_limit + price_max_margin)
  224. rule_id = rule.id
  225. break
  226. # Final price conversion to target UoM
  227. price = product_uom_obj._compute_price(
  228. cr,
  229. uid,
  230. price_uom_id,
  231. price,
  232. qty_uom_id
  233. )
  234. results[product.id] = (price, rule_id)
  235. return results