GoodERP
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

258 lignes
12KB

  1. # Copyright 2016 上海开阖软件有限公司 (http://www.osbzr.com)
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. from odoo import fields, models, api
  4. from odoo.exceptions import UserError
  5. from odoo.tools import float_compare
  6. # 订单确认状态可选值
  7. BUY_ORDER_STATES = [
  8. ('draft', '草稿'),
  9. ('done', '已确认'),
  10. ('cancel', '已作废')]
  11. # 字段只读状态
  12. READONLY_STATES = {
  13. 'done': [('readonly', True)],
  14. 'cancel': [('readonly', True)],
  15. }
  16. class BuyAdjust(models.Model):
  17. _name = "buy.adjust"
  18. _inherit = ['mail.thread']
  19. _description = "采购变更单"
  20. _order = 'date desc, id desc'
  21. name = fields.Char('单据编号', copy=False,
  22. help='变更单编号,保存时可自动生成')
  23. order_id = fields.Many2one('buy.order', '原始单据', states=READONLY_STATES,
  24. copy=False, ondelete='restrict',
  25. help='要调整的原始采购订单,只能调整已确认且没有全部入库的采购订单')
  26. date = fields.Date('单据日期', states=READONLY_STATES,
  27. default=lambda self: fields.Date.context_today(self),
  28. index=True, copy=False,
  29. help='变更单创建日期,默认是当前日期')
  30. line_ids = fields.One2many('buy.adjust.line', 'order_id', '变更单行',
  31. states=READONLY_STATES, copy=True,
  32. help='变更单明细行,不允许为空')
  33. approve_uid = fields.Many2one('res.users', '确认人',
  34. copy=False, ondelete='restrict',
  35. help='确认变更单的人')
  36. state = fields.Selection(BUY_ORDER_STATES, '确认状态',
  37. index=True, copy=False,
  38. tracking=True,
  39. default='draft',
  40. help='变更单确认状态')
  41. note = fields.Text('备注',
  42. help='单据备注')
  43. user_id = fields.Many2one(
  44. 'res.users',
  45. '经办人',
  46. ondelete='restrict',
  47. states=READONLY_STATES,
  48. default=lambda self: self.env.user,
  49. help='单据经办人',
  50. )
  51. company_id = fields.Many2one(
  52. 'res.company',
  53. string='公司',
  54. change_default=True,
  55. default=lambda self: self.env.company)
  56. def _get_vals(self, line):
  57. '''返回创建 buy order line 时所需数据'''
  58. return {
  59. 'order_id': self.order_id.id,
  60. 'goods_id': line.goods_id.id,
  61. 'attribute_id': line.attribute_id.id,
  62. 'quantity': line.quantity,
  63. 'uom_id': line.uom_id.id,
  64. 'price_taxed': line.price_taxed,
  65. 'discount_rate': line.discount_rate,
  66. 'discount_amount': line.discount_amount,
  67. 'tax_rate': line.tax_rate,
  68. 'note': line.note or '',
  69. }
  70. def buy_adjust_done(self):
  71. '''确认采购变更单:
  72. 当调整后数量 < 原单据中已入库数量,则报错;
  73. 当调整后数量 > 原单据中已入库数量,则更新原单据及入库单分单的数量;
  74. 当调整后数量 = 原单据中已入库数量,则更新原单据数量,删除入库单行;
  75. 当新增商品时,则更新原单据及入库单分单明细行。
  76. '''
  77. self.ensure_one()
  78. if self.state == 'done':
  79. raise UserError('请不要重复确认!\n采购变更单%s已确认' % self.name)
  80. if not self.line_ids:
  81. raise UserError('请输入商品明细行!')
  82. for line in self.line_ids:
  83. if line.price_taxed < 0:
  84. raise UserError('商品含税单价不能小于0!\n单价:%s' % line.price_taxed)
  85. buy_receipt = self.env['buy.receipt'].search(
  86. [('order_id', '=', self.order_id.id),
  87. ('state', '=', 'draft')])
  88. if not buy_receipt:
  89. raise UserError('采购入库单已全部入库,不能调整')
  90. for line in self.line_ids:
  91. # 检查属性是否填充,防止无权限人员不填就可以保存
  92. if line.using_attribute and not line.attribute_id:
  93. raise UserError('请输入商品:%s 的属性' % line.goods_id.name)
  94. origin_line = self.env['buy.order.line'].search(
  95. [('goods_id', '=', line.goods_id.id),
  96. ('attribute_id', '=', line.attribute_id.id),
  97. ('order_id', '=', self.order_id.id)])
  98. if len(origin_line) > 1:
  99. raise UserError('要调整的商品%s在原始单据中不唯一' % line.goods_id.name)
  100. if origin_line:
  101. origin_line.quantity += line.quantity # 调整后数量
  102. new_note = '变更单:%s %s。\n' % (self.name, line.note)
  103. origin_line.note = (origin_line.note and
  104. origin_line.note + new_note or new_note)
  105. if origin_line.quantity < origin_line.quantity_in:
  106. raise UserError('%s调整后数量不能小于原订单已入库数量' %
  107. line.goods_id.name)
  108. elif origin_line.quantity > origin_line.quantity_in:
  109. # 查找出原采购订单产生的草稿状态的入库单明细行,并更新它
  110. move_line = self.env['wh.move.line'].search(
  111. [('buy_line_id', '=', origin_line.id),
  112. ('state', '=', 'draft')])
  113. if move_line:
  114. move_line.goods_qty += line.quantity
  115. else:
  116. raise UserError('商品%s已全部入库,建议新建采购订单' %
  117. line.goods_id.name)
  118. # 调整后数量与已入库数量相等时,删除产生的入库单分单
  119. else:
  120. # 删除对应行
  121. move_line = self.env['wh.move.line'].search(
  122. [('buy_line_id', '=', origin_line.id),
  123. ('state', '=', 'draft')])
  124. move_line.unlink()
  125. else:
  126. new_line = self.env['buy.order.line'].create(
  127. self._get_vals(line))
  128. receipt_line = []
  129. if line.goods_id.force_batch_one:
  130. i = 0
  131. while i < line.quantity:
  132. i += 1
  133. receipt_line.append(
  134. self.order_id.get_receipt_line(new_line, single=True))
  135. else:
  136. receipt_line.append(
  137. self.order_id.get_receipt_line(new_line, single=False))
  138. buy_receipt.write(
  139. {'line_in_ids': [(0, 0, li) for li in receipt_line]})
  140. if not buy_receipt.line_in_ids:
  141. buy_receipt.unlink()
  142. self.state = 'done'
  143. self.approve_uid = self._uid
  144. class BuyAdjustLine(models.Model):
  145. _name = 'buy.adjust.line'
  146. _description = '采购变更单明细'
  147. @api.depends('goods_id')
  148. def _compute_using_attribute(selfs):
  149. '''返回订单行中商品是否使用属性'''
  150. for self in selfs:
  151. self.using_attribute = self.goods_id.attribute_ids and True or False
  152. @api.depends('quantity', 'price_taxed', 'discount_amount', 'tax_rate')
  153. def _compute_all_amount(selfs):
  154. '''当订单行的数量、单价、折扣额、税率改变时,改变采购金额、税额、价税合计'''
  155. for self in selfs:
  156. self.subtotal = self.price_taxed * self.quantity - self.discount_amount # 价税合计
  157. self.tax_amount = self.subtotal / \
  158. (100 + self.tax_rate) * self.tax_rate # 税额
  159. self.amount = self.subtotal - self.tax_amount # 金额
  160. @api.onchange('price', 'tax_rate')
  161. def onchange_price(self):
  162. '''当订单行的不含税单价改变时,改变含税单价'''
  163. price = self.price_taxed / (1 + self.tax_rate * 0.01) # 不含税单价
  164. decimal = self.env.ref('core.decimal_price')
  165. if float_compare(price, self.price, precision_digits=decimal.digits) != 0:
  166. self.price_taxed = self.price * (1 + self.tax_rate * 0.01)
  167. order_id = fields.Many2one('buy.adjust', '订单编号', index=True,
  168. required=True, ondelete='cascade',
  169. help='关联的变更单编号')
  170. goods_id = fields.Many2one('goods', '商品', ondelete='restrict',
  171. help='商品')
  172. using_attribute = fields.Boolean('使用属性', compute=_compute_using_attribute,
  173. help='商品是否使用属性')
  174. attribute_id = fields.Many2one('attribute', '属性',
  175. ondelete='restrict',
  176. domain="[('goods_id', '=', goods_id)]",
  177. help='商品的属性,当商品有属性时,该字段必输')
  178. uom_id = fields.Many2one('uom', '单位', ondelete='restrict',
  179. help='商品计量单位')
  180. quantity = fields.Float('调整数量',
  181. default=1,
  182. required=True,
  183. digits='Quantity',
  184. help='相对于原单据对应明细行的调整数量,可正可负')
  185. price = fields.Float('采购单价',
  186. store=True,
  187. digits='Price',
  188. help='不含税单价,由含税单价计算得出')
  189. price_taxed = fields.Float('含税单价',
  190. digits='Price',
  191. help='含税单价,取自商品成本')
  192. discount_rate = fields.Float('折扣率%',
  193. help='折扣率')
  194. discount_amount = fields.Float('折扣额',
  195. digits='Amount',
  196. help='输入折扣率后自动计算得出,也可手动输入折扣额')
  197. amount = fields.Float('金额', compute=_compute_all_amount,
  198. store=True, readonly=True,
  199. digits='Amount',
  200. help='金额 = 价税合计 - 税额')
  201. tax_rate = fields.Float('税率(%)', default=lambda self: self.env.user.company_id.import_tax_rate,
  202. help='默认值取公司进项税率')
  203. tax_amount = fields.Float('税额', compute=_compute_all_amount,
  204. store=True, readonly=True,
  205. digits='Amount',
  206. help='由税率计算得出')
  207. subtotal = fields.Float('价税合计', compute=_compute_all_amount,
  208. store=True, readonly=True,
  209. digits='Amount',
  210. help='含税单价 乘以 数量')
  211. note = fields.Char('备注',
  212. help='本行备注')
  213. company_id = fields.Many2one(
  214. 'res.company',
  215. string='公司',
  216. change_default=True,
  217. default=lambda self: self.env.company)
  218. @api.onchange('goods_id')
  219. def onchange_goods_id(self):
  220. '''当订单行的商品变化时,带出商品上的单位、默认仓库、成本价'''
  221. if self.goods_id:
  222. self.uom_id = self.goods_id.uom_id
  223. self.price_taxed = self.goods_id.cost
  224. self.tax_rate = self.goods_id.get_tax_rate(self.goods_id, self.order_id.order_id.partner_id, 'buy')
  225. @api.onchange('quantity', 'price_taxed', 'discount_rate')
  226. def onchange_discount_rate(self):
  227. '''当数量、单价或优惠率发生变化时,优惠金额发生变化'''
  228. self.price = self.price_taxed / (1 + self.tax_rate * 0.01)
  229. self.discount_amount = (self.quantity * self.price *
  230. self.discount_rate * 0.01)
  231. @api.constrains('tax_rate')
  232. def _check(selfs):
  233. for self in selfs:
  234. if self.tax_rate > 100:
  235. raise UserError('税率不能输入超过100的数\n税率:%s!' % self.tax_rate)
  236. if self.tax_rate < 0:
  237. raise UserError('税率不能输入负数\n税率:%s!' % self.tax_rate)
上海开阖软件有限公司 沪ICP备12045867号-1