GoodERP
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

747 lines
32KB

  1. from odoo import fields, models, api
  2. from odoo.exceptions import UserError
  3. from odoo.tools import float_compare
  4. import logging
  5. from odoo.tools.translate import _
  6. _logger = logging.getLogger(__name__)
  7. # 销售订单确认状态可选值
  8. SELL_ORDER_STATES = [
  9. ('draft', '草稿'),
  10. ('done', '已确认'),
  11. ('cancel', '已作废')]
  12. class SellOrder(models.Model):
  13. _name = 'sell.order'
  14. _description = '销售订单'
  15. _inherit = ['mail.thread']
  16. _order = 'date desc, id desc'
  17. @api.depends('line_ids.subtotal', 'discount_amount')
  18. def _compute_amount(self):
  19. """当订单行和优惠金额改变时,改变成交金额"""
  20. for o in self:
  21. total = sum(line.subtotal for line in o.line_ids)
  22. o.amount = total - o.discount_amount
  23. o.untax_amount = sum(line.amount for line in o.line_ids)
  24. o.tax_amount = sum(line.tax_amount for line in o.line_ids)
  25. @api.depends('line_ids.quantity')
  26. def _compute_qty(self):
  27. """当订单行数量改变时,更新总数量"""
  28. for o in self:
  29. o.total_qty = sum(line.quantity for line in o.line_ids)
  30. @api.depends('delivery_ids.state')
  31. def _get_sell_goods_state(selfs):
  32. '''返回发货状态'''
  33. for self in selfs:
  34. if all(line.quantity_out == 0 for line in self.line_ids):
  35. if any(r.state == 'draft' for r in self.delivery_ids) or self.state=='draft':
  36. self.goods_state = '未出库'
  37. else:
  38. self.goods_state = '全部作废'
  39. elif any(line.quantity > line.quantity_out for line in self.line_ids):
  40. if any(r.state == 'draft' for r in self.delivery_ids):
  41. self.goods_state = '部分出库'
  42. else:
  43. self.goods_state = '部分出库剩余作废'
  44. else:
  45. self.goods_state = '全部出库'
  46. @api.depends('partner_id')
  47. def _compute_currency_id(self):
  48. for o in self:
  49. self.currency_id = \
  50. self.partner_id.c_category_id.account_id.currency_id.id \
  51. or self.partner_id.s_category_id.account_id.currency_id.id
  52. @api.model
  53. def _default_warehouse(self):
  54. return self._default_warehouse_impl()
  55. @api.model
  56. def _default_warehouse_impl(self):
  57. if self.env.context.get('warehouse_type'):
  58. return self.env['warehouse'].get_warehouse_by_type(
  59. self.env.context.get('warehouse_type'), False)
  60. def _get_received_amount(selfs):
  61. '''计算销售订单收款/退款状态'''
  62. for self in selfs:
  63. deliverys = self.env['sell.delivery'].search(
  64. [('order_id', '=', self.id)])
  65. money_order_rows = self.env['money.order'].search([('sell_id', '=', self.id),
  66. ('reconciled', '=', 0),
  67. ('state', '=', 'done')])
  68. self.received_amount = sum([delivery.invoice_id.reconciled for delivery in deliverys]) +\
  69. sum([order_row.amount for order_row in money_order_rows])
  70. @api.depends('delivery_ids')
  71. def _compute_delivery(self):
  72. for order in self:
  73. order.delivery_count = len([deli for deli in order.delivery_ids if not deli.is_return])
  74. order.return_count = len([deli for deli in order.delivery_ids if deli.is_return])
  75. @api.depends('partner_id')
  76. def _get_sell_user(selfs):
  77. '''计算销售单据的业务员,不允许修改'''
  78. for self in selfs:
  79. if self.partner_id:
  80. if self.partner_id.responsible_id:
  81. self.user_id = self.partner_id.responsible_id
  82. else:
  83. self.user_id = self._uid
  84. @api.depends('line_ids.goods_id', 'line_ids.quantity')
  85. def _compute_net_weight(self):
  86. '''计算净重合计'''
  87. for o in self:
  88. o.net_weight = sum(
  89. line.goods_id.net_weight * line.quantity
  90. for line in o.line_ids
  91. )
  92. partner_id = fields.Many2one('partner', '客户',
  93. ondelete='restrict',
  94. help='签约合同的客户')
  95. contact = fields.Char('联系人',
  96. help='客户方的联系人')
  97. address_id = fields.Many2one('partner.address', '地址',
  98. domain="[('partner_id', '=', partner_id)]",
  99. help='联系地址')
  100. mobile = fields.Char('手机',
  101. help='联系手机')
  102. user_id = fields.Many2one(
  103. 'res.users',
  104. '销售员',
  105. ondelete='restrict',store=True,
  106. compute='_get_sell_user',
  107. help='单据经办人',
  108. )
  109. date = fields.Date('单据日期',
  110. required=True,
  111. default=lambda self: fields.Date.context_today(self),
  112. index=True,
  113. copy=False,
  114. help="默认是订单创建日期")
  115. delivery_date = fields.Date(
  116. '要求交货日期',
  117. required=True,
  118. default=lambda self: fields.Date.context_today(self),
  119. index=True,
  120. copy=False,
  121. help="订单的要求交货日期")
  122. type = fields.Selection([('sell', '销售'), ('return', '退货')], '类型',
  123. default='sell',
  124. help='销售订单的类型,分为销售或退货')
  125. ref = fields.Char('客户订单号')
  126. warehouse_id = fields.Many2one('warehouse',
  127. '调出仓库',
  128. required=True,
  129. ondelete='restrict',
  130. default=_default_warehouse,
  131. help='商品将从该仓库调出')
  132. name = fields.Char('单据编号', index=True, copy=False,
  133. default='/', help="创建时它会自动生成下一个编号")
  134. line_ids = fields.One2many('sell.order.line', 'order_id', '销售订单行',
  135. copy=True,
  136. help='销售订单的明细行,不能为空')
  137. note = fields.Text('备注', help='单据备注')
  138. discount_rate = fields.Float('优惠率(%)',
  139. help='整单优惠率')
  140. discount_amount = fields.Float('抹零',
  141. digits='Amount',
  142. help='整单优惠金额,可由优惠率自动计算出来,也可手动输入')
  143. amount = fields.Float(string='成交金额', store=True, readonly=True,
  144. compute='_compute_amount',
  145. digits='Amount',
  146. help='总金额减去优惠金额')
  147. tax_amount = fields.Float(string='税额', store=True, readonly=True,
  148. compute='_compute_amount',
  149. digits='Amount',
  150. help='税额')
  151. untax_amount = fields.Float(string='不含税金额', store=True, readonly=True,
  152. compute='_compute_amount',
  153. digits='Amount',
  154. help='不含税金额')
  155. total_qty = fields.Float(string='数量合计', store=True, readonly=True, copy=False,
  156. compute='_compute_qty',
  157. digits='Quantity',
  158. help='数量总计')
  159. pre_receipt = fields.Float('预收款',
  160. digits='Amount',
  161. help='输入预收款确认销售订单,会产生一张收款单')
  162. bank_account_id = fields.Many2one('bank.account', '结算账户',
  163. ondelete='restrict',
  164. help='用来核算和监督企业与其他单位或个人之间的债权债务的结算情况')
  165. approve_uid = fields.Many2one('res.users', '确认人', copy=False,
  166. ondelete='restrict',
  167. help='确认单据的人')
  168. state = fields.Selection(SELL_ORDER_STATES, '确认状态', readonly=True,
  169. help="销售订单的确认状态", index=True,
  170. tracking=True,
  171. copy=False, default='draft')
  172. goods_state = fields.Char('发货状态', compute=_get_sell_goods_state,
  173. default='未出库',
  174. store=True,
  175. help="销售订单的发货状态", index=True, copy=False)
  176. cancelled = fields.Boolean('已终止',
  177. help='该单据是否已终止')
  178. currency_id = fields.Many2one('res.currency',
  179. '外币币别',
  180. compute='_compute_currency_id',
  181. store=True,
  182. readonly=True,
  183. help='外币币别')
  184. is_multi_currency = fields.Boolean('多币种', related='company_id.is_multi_currency')
  185. pay_base_currency = fields.Boolean('以本币结算', help='客户以本币付款到我公司基本账户内')
  186. company_id = fields.Many2one(
  187. 'res.company',
  188. string='公司',
  189. change_default=True,
  190. default=lambda self: self.env.company)
  191. received_amount = fields.Float(
  192. '已收金额', compute=_get_received_amount, readonly=True)
  193. delivery_ids = fields.One2many(
  194. 'sell.delivery', 'order_id', string='发货单', copy=False)
  195. delivery_count = fields.Integer(
  196. compute='_compute_delivery', string='发货单数量', default=0)
  197. return_count = fields.Integer(
  198. compute='_compute_delivery', string='退货单数量', default=0)
  199. pay_method = fields.Many2one('pay.method',
  200. string='付款方式',
  201. ondelete='restrict')
  202. term_id = fields.Many2one('core.value', "贸易条款",
  203. domain=[('type', '=', 'price_term')],
  204. context={'type': 'price_term'}
  205. )
  206. pol = fields.Char('起运港')
  207. pod = fields.Char('目的港')
  208. express_type = fields.Char('承运商')
  209. money_order_id = fields.Many2one(
  210. 'money.order',
  211. '预收款单',
  212. readonly=True,
  213. copy=False,
  214. help='输入预收款确认时产生的预收款单')
  215. net_weight = fields.Float(
  216. string='净重合计', compute='_compute_net_weight', store=True)
  217. invoice_ids = fields.One2many(
  218. 'money.invoice', compute='_compute_invoice', string='Invoices')
  219. invoice_count = fields.Integer(
  220. compute='_compute_invoice', string='Invoices Count', default=0)
  221. details = fields.Html('明细',compute='_compute_details')
  222. paid_no_goods = fields.Boolean('已收款未发货',compute="_compute_paid_no_goods",store=True)
  223. goods_id = fields.Many2one(
  224. 'goods', related='line_ids.goods_id', string='商品') #用于在列表上根据商品搜索
  225. project_id = fields.Many2one('project', string='项目')
  226. @api.depends('money_order_id.state','goods_state')
  227. def _compute_paid_no_goods(self):
  228. for o in self:
  229. o.paid_no_goods = False
  230. if o.state == 'done' and o.goods_state == '未出库' and o.received_amount:
  231. if not all(line.goods_id.no_stock for line in self.line_ids):
  232. o.paid_no_goods = True
  233. @api.depends('line_ids')
  234. def _compute_details(self):
  235. for v in self:
  236. vl = {'col':[],'val':[]}
  237. vl['col'] = [
  238. _('商品'),
  239. _('数量'),
  240. _('单价'),
  241. _('已发')
  242. ]
  243. for l in v.line_ids:
  244. vl['val'].append([l.goods_id.name,l.quantity,l.price,l.quantity_out])
  245. v.details = v.company_id._get_html_table(vl)
  246. @api.onchange('address_id')
  247. def onchange_partner_address(self):
  248. ''' 选择地址填充 联系人、电话 '''
  249. if self.address_id:
  250. self.contact = self.address_id.contact
  251. self.mobile = self.address_id.mobile
  252. @api.onchange('partner_id')
  253. def onchange_partner_id(self):
  254. ''' 选择客户带出其默认地址信息 '''
  255. if self.partner_id:
  256. self.contact = self.partner_id.contact
  257. self.mobile = self.partner_id.mobile
  258. self.pay_method = self.partner_id.pay_method
  259. for child in self.partner_id.child_ids:
  260. if child.is_default_add:
  261. self.address_id = child.id
  262. if self.partner_id.child_ids and not any([child.is_default_add for child in self.partner_id.child_ids]):
  263. partners_add = self.env['partner.address'].search(
  264. [('partner_id', '=', self.partner_id.id)], order='id')
  265. self.address_id = partners_add[0].id
  266. for line in self.line_ids:
  267. line.tax_rate = line.goods_id.get_tax_rate(line.goods_id, self.partner_id, 'sell')
  268. address_list = [
  269. child_list.id for child_list in self.partner_id.child_ids]
  270. if address_list:
  271. return {'domain': {'address_id': [('id', 'in', address_list)]}}
  272. else:
  273. self.address_id = False
  274. @api.onchange('discount_rate', 'line_ids')
  275. def onchange_discount_rate(self):
  276. '''当优惠率或销售订单行发生变化时,单据优惠金额发生变化'''
  277. total = sum(line.subtotal for line in self.line_ids)
  278. self.discount_amount = total * self.discount_rate * 0.01
  279. def _get_vals(self):
  280. '''返回创建 money_order 时所需数据'''
  281. flag = (self.type == 'sell' and 1 or -1) # 用来标志发库或退货
  282. amount = flag * self.amount
  283. this_reconcile = flag * self.pre_receipt
  284. money_lines = [{
  285. 'bank_id': self.bank_account_id.id,
  286. 'amount': this_reconcile,
  287. }]
  288. return {
  289. 'partner_id': self.partner_id.id,
  290. 'date': fields.Date.context_today(self),
  291. 'line_ids':
  292. [(0, 0, line) for line in money_lines],
  293. 'amount': amount,
  294. 'reconciled': this_reconcile,
  295. 'to_reconcile': amount,
  296. 'state': 'draft',
  297. 'origin_name': self.name,
  298. 'sell_id': self.id,
  299. }
  300. def generate_receipt_order(self):
  301. '''由销售订单生成收款单'''
  302. # 发库单/退货单
  303. if self.pre_receipt:
  304. money_order = self.with_context(type='get').env['money.order'].create(
  305. self._get_vals()
  306. )
  307. return money_order
  308. def sell_order_done(self):
  309. '''确认销售订单'''
  310. self.ensure_one()
  311. if self.state == 'done':
  312. raise UserError('请不要重复确认!')
  313. if not self.line_ids:
  314. raise UserError('请输入商品明细行!')
  315. for line in self.line_ids:
  316. # 检查属性是否填充,防止无权限人员不填就可以保存
  317. if line.using_attribute and not line.attribute_id:
  318. raise UserError('请输入商品:%s 的属性' % line.goods_id.name)
  319. if line.quantity <= 0 or line.price_taxed < 0:
  320. raise UserError('商品 %s 的数量和含税单价不能小于0!' % line.goods_id.name)
  321. if line.tax_amount > 0 and self.currency_id:
  322. raise UserError('外贸免税!')
  323. if not self.bank_account_id and self.pre_receipt:
  324. raise UserError('预付款不为空时,请选择结算账户!')
  325. # 销售预收款生成收款单
  326. money_order = self.generate_receipt_order()
  327. self.sell_generate_delivery()
  328. self.approve_uid = self._uid
  329. self.write({
  330. 'money_order_id': money_order and money_order.id,
  331. 'state': 'done', # 为保证审批流程顺畅,否则,未审批就可审核
  332. })
  333. return True
  334. def sell_order_draft(self):
  335. '''撤销确认销售订单'''
  336. self.ensure_one()
  337. if self.state == 'draft':
  338. raise UserError('请不要重复撤销 %s' % self._description)
  339. if any(r.state == 'done' for r in self.delivery_ids):
  340. raise UserError('该销售订单已经发货,不能撤销确认!')
  341. # 查找产生的发货单并删除
  342. self.delivery_ids.unlink()
  343. # 查找产生的收款单并删除
  344. if self.money_order_id:
  345. self.money_order_id.unlink()
  346. self.approve_uid = False
  347. self.state = 'draft'
  348. def get_delivery_line(self, line, single=False):
  349. '''返回销售发货/退货单行'''
  350. self.ensure_one()
  351. qty = 0
  352. discount_amount = 0
  353. if single:
  354. qty = 1
  355. discount_amount = line.discount_amount \
  356. / ((line.quantity - line.quantity_out) or 1)
  357. else:
  358. qty = line.quantity - line.quantity_out
  359. discount_amount = line.discount_amount
  360. return {
  361. 'type': self.type == 'sell' and 'out' or 'in',
  362. 'sell_line_id': line.id,
  363. 'goods_id': line.goods_id.id,
  364. 'attribute_id': line.attribute_id.id,
  365. 'uos_id': line.goods_id.uos_id.id,
  366. 'goods_qty': qty,
  367. 'uom_id': line.uom_id.id,
  368. 'cost_unit': line.goods_id.cost,
  369. 'price': line.price,
  370. 'price_taxed': line.price_taxed,
  371. 'discount_rate': line.discount_rate,
  372. 'discount_amount': discount_amount,
  373. 'tax_rate': line.tax_rate,
  374. 'plan_date':self.delivery_date,
  375. }
  376. def _generate_delivery(self, delivery_line):
  377. '''根据明细行生成发货单或退货单'''
  378. # 如果退货,warehouse_dest_id,warehouse_id要调换
  379. warehouse = (self.type == 'sell'
  380. and self.warehouse_id
  381. or self.env.ref("warehouse.warehouse_customer"))
  382. warehouse_dest = (self.type == 'sell'
  383. and self.env.ref("warehouse.warehouse_customer")
  384. or self.warehouse_id)
  385. rec = (self.type == 'sell' and self.with_context(is_return=False)
  386. or self.with_context(is_return=True))
  387. delivery_id = rec.env['sell.delivery'].create({
  388. 'partner_id': self.partner_id.id,
  389. 'warehouse_id': warehouse.id,
  390. 'warehouse_dest_id': warehouse_dest.id,
  391. 'user_id': self.user_id.id,
  392. 'date': self.delivery_date,
  393. 'order_id': self.id,
  394. 'ref':self.ref,
  395. 'origin': 'sell.delivery',
  396. 'discount_rate': self.discount_rate,
  397. 'discount_amount': self.discount_amount,
  398. 'currency_id': self.currency_id.id,
  399. 'contact': self.contact,
  400. 'address_id': self.address_id.id,
  401. 'mobile': self.mobile,
  402. 'express_type': self.express_type,
  403. 'project_id': self.project_id.id,
  404. })
  405. if self.type == 'sell':
  406. delivery_id.write({'line_out_ids': [
  407. (0, 0, line) for line in delivery_line]})
  408. else:
  409. delivery_id.write({'line_in_ids': [
  410. (0, 0, line) for line in delivery_line]})
  411. return delivery_id
  412. def sell_generate_delivery(self):
  413. '''由销售订单生成销售发货单'''
  414. self.ensure_one()
  415. delivery_line = [] # 销售发货单行
  416. for line in self.line_ids:
  417. # 如果订单部分出库,则点击此按钮时生成剩余数量的出库单
  418. to_out = line.quantity - line.quantity_out
  419. if to_out <= 0:
  420. continue
  421. if line.goods_id.force_batch_one:
  422. i = 0
  423. while i < to_out:
  424. i += 1
  425. delivery_line.append(
  426. self.get_delivery_line(line, single=True))
  427. else:
  428. delivery_line.append(
  429. self.get_delivery_line(line, single=False))
  430. if not delivery_line:
  431. return {}
  432. self._generate_delivery(delivery_line)
  433. return {}
  434. @api.depends('delivery_ids')
  435. def _compute_invoice(self):
  436. for order in self:
  437. money_invoices = self.env['money.invoice'].search([
  438. ('name', '=', order.name)])
  439. order.invoice_ids = not money_invoices and order.delivery_ids.mapped('invoice_id') or money_invoices + order.delivery_ids.mapped('invoice_id')
  440. order.invoice_count = len(order.invoice_ids.ids)
  441. def action_view_invoice(self):
  442. self.ensure_one()
  443. if self.invoice_count == 0:
  444. return False
  445. action = {
  446. 'name': '结算单(客户发票)',
  447. 'type': 'ir.actions.act_window',
  448. 'view_mode': 'form',
  449. 'res_model': 'money.invoice',
  450. 'view_id': False,
  451. 'target': 'current',
  452. }
  453. invoice_ids = self.invoice_ids.ids
  454. # choose the view_mode accordingly
  455. action['domain'] = "[('id','in',[" + \
  456. ','.join(map(str, invoice_ids)) + "])]"
  457. action['view_mode'] = 'list'
  458. return action
  459. def action_view_delivery(self):
  460. '''
  461. This function returns an action that display existing deliverys of given sells order ids.
  462. When only one found, show the delivery immediately.
  463. '''
  464. self.ensure_one()
  465. action = {
  466. 'name': '销售发货单',
  467. 'type': 'ir.actions.act_window',
  468. 'view_mode': 'form',
  469. 'res_model': 'sell.delivery',
  470. 'view_id': False,
  471. 'target': 'current',
  472. }
  473. delivery_ids = [delivery.id for delivery in self.delivery_ids if not delivery.is_return]
  474. if len(delivery_ids) > 1:
  475. action['domain'] = "[('id','in',[" + \
  476. ','.join(map(str, delivery_ids)) + "])]"
  477. action['view_mode'] = 'list,form'
  478. elif len(delivery_ids) == 1:
  479. view_id = self.env.ref('sell.sell_delivery_form').id
  480. action['views'] = [(view_id, 'form')]
  481. action['res_id'] = delivery_ids and delivery_ids[0] or False
  482. return action
  483. def action_view_return(self):
  484. '''
  485. 该销售订单对应的退货单
  486. '''
  487. self.ensure_one()
  488. action = {
  489. 'name': '销售退货单',
  490. 'type': 'ir.actions.act_window',
  491. 'view_type': 'form',
  492. 'view_mode': 'form',
  493. 'res_model': 'sell.delivery',
  494. 'view_id': False,
  495. 'target': 'current',
  496. }
  497. list_view_id = self.env.ref('sell.sell_return_list').id
  498. form_view_id = self.env.ref('sell.sell_return_form').id
  499. delivery_ids = [delivery.id for delivery in self.delivery_ids if delivery.is_return]
  500. if len(delivery_ids) > 1:
  501. action['domain'] = "[('id','in',[" + \
  502. ','.join(map(str, delivery_ids)) + "])]"
  503. action['view_mode'] = 'list,form'
  504. action['views'] = [(list_view_id, 'list'), (form_view_id, 'form')]
  505. elif len(delivery_ids) == 1:
  506. action['views'] = [(form_view_id, 'form')]
  507. action['res_id'] = delivery_ids and delivery_ids[0] or False
  508. return action
  509. class SellOrderLine(models.Model):
  510. _name = 'sell.order.line'
  511. _description = '销售订单明细'
  512. @api.depends('goods_id')
  513. def _compute_using_attribute(self):
  514. '''返回订单行中商品是否使用属性'''
  515. for l in self:
  516. l.using_attribute = l.goods_id.attribute_ids and True or False
  517. @api.depends('quantity', 'price_taxed', 'discount_amount', 'tax_rate')
  518. def _compute_all_amount(selfs):
  519. '''当订单行的数量、含税单价、折扣额、税率改变时,改变销售金额、税额、价税合计'''
  520. for self in selfs:
  521. if self.order_id.currency_id.id == self.env.user.company_id.currency_id.id:
  522. self.subtotal = self.price_taxed * self.quantity - self.discount_amount # 价税合计
  523. self.tax_amount = self.subtotal / \
  524. (100 + self.tax_rate) * self.tax_rate # 税额
  525. self.amount = self.subtotal - self.tax_amount # 金额
  526. else:
  527. rate_silent = self.env['res.currency'].get_rate_silent(
  528. self.order_id.date, self.order_id.currency_id.id) or 1
  529. if not self.order_id.pay_base_currency:
  530. rate_silent = 1
  531. self.subtotal = (self.price_taxed * self.quantity -
  532. self.discount_amount) * rate_silent # 价税合计
  533. self.tax_amount = self.subtotal / \
  534. (100 + self.tax_rate) * self.tax_rate # 税额
  535. self.amount = self.subtotal - self.tax_amount # 本位币金额
  536. @api.onchange('price', 'tax_rate')
  537. def onchange_price(self):
  538. '''当订单行的不含税单价改变时,改变含税单价。
  539. 如果将含税价改为99,则self.price计算出来为84.62,price=99/1.17,
  540. 跟84.62保留相同位数比较时是相等的,这种情况则保留含税价不变,
  541. 这样处理是为了使得修改含税价时不再重新计算含税价。
  542. '''
  543. _logger.info('单价或税率发生变化')
  544. price = self.price_taxed / (1 + self.tax_rate * 0.01) # 不含税单价
  545. decimal = self.env.ref('core.decimal_price')
  546. if float_compare(price, self.price, precision_digits=decimal.digits) != 0:
  547. self.price_taxed = self.price * (1 + self.tax_rate * 0.01)
  548. order_id = fields.Many2one('sell.order', '订单编号', index=True,
  549. required=True, ondelete='cascade',
  550. help='关联订单的编号')
  551. partner_id = fields.Many2one(
  552. 'partner',
  553. string="客户",
  554. related='order_id.partner_id',
  555. store=True)
  556. goods_id = fields.Many2one('goods',
  557. '商品',
  558. required=True,
  559. ondelete='restrict',
  560. help='商品')
  561. using_attribute = fields.Boolean('使用属性', compute=_compute_using_attribute,
  562. help='商品是否使用属性')
  563. attribute_id = fields.Many2one('attribute', '属性',
  564. ondelete='restrict',
  565. domain="[('goods_id', '=', goods_id)]",
  566. help='商品的属性,当商品有属性时,该字段必输')
  567. uom_id = fields.Many2one('uom', '单位', ondelete='restrict',
  568. help='商品计量单位')
  569. quantity = fields.Float('数量',
  570. default=1,
  571. required=True,
  572. digits='Quantity',
  573. help='下单数量')
  574. quantity_out = fields.Float('已执行数量', copy=False,
  575. digits='Quantity',
  576. help='销售订单产生的发货单/退货单已执行数量')
  577. price = fields.Float('销售单价',
  578. store=True,
  579. digits='Price',
  580. help='不含税单价,由含税单价计算得出')
  581. price_taxed = fields.Float('含税单价',
  582. digits='Price',
  583. help='含税单价,取商品零售价')
  584. discount_rate = fields.Float('折扣率%',
  585. help='折扣率')
  586. discount_amount = fields.Float('折扣额',
  587. help='输入折扣率后自动计算得出,也可手动输入折扣额')
  588. amount = fields.Float('金额',
  589. compute=_compute_all_amount,
  590. store=True,
  591. digits='Amount',
  592. help='金额 = 价税合计 - 税额')
  593. tax_rate = fields.Float('税率(%)',
  594. help='税率')
  595. tax_amount = fields.Float('税额',
  596. compute=_compute_all_amount,
  597. store=True,
  598. digits='Amount',
  599. help='税额')
  600. subtotal = fields.Float('价税合计',
  601. compute=_compute_all_amount,
  602. store=True,
  603. digits='Amount',
  604. help='含税单价 乘以 数量')
  605. note = fields.Char('备注',
  606. help='本行备注')
  607. company_id = fields.Many2one(
  608. 'res.company', string='公司',
  609. change_default=True,
  610. default=lambda self: self.env.company)
  611. # 销售订单行上增加订单的重要字段以便销售订单明细表界面可针对其筛选分组
  612. order_date = fields.Date(related='order_id.date', string='订单日期', store=True)
  613. order_state = fields.Selection(related='order_id.state', string='订单状态', store=True)
  614. order_currency = fields.Many2one('res.currency', related='order_id.currency_id', string='订单币种', store=True)
  615. quantity_todo = fields.Float(
  616. '未执行数量', compute="_compute_quantity_todo",
  617. store=True, digits='Quantity')
  618. @api.depends('quantity_out', 'quantity')
  619. def _compute_quantity_todo(self):
  620. for s in self:
  621. s.quantity_todo = s.quantity - s.quantity_out
  622. @api.onchange('goods_id')
  623. def onchange_warehouse_id(self):
  624. '''当订单行的仓库变化时,带出定价策略中的折扣率'''
  625. if self.order_id.warehouse_id and self.goods_id:
  626. partner = self.order_id.partner_id
  627. warehouse = self.order_id.warehouse_id
  628. goods = self.goods_id
  629. date = self.order_id.date
  630. pricing = self.env['pricing'].get_pricing_id(
  631. partner, warehouse, goods, date)
  632. if pricing:
  633. self.discount_rate = pricing.discount_rate
  634. else:
  635. self.discount_rate = 0
  636. @api.onchange('goods_id')
  637. def onchange_goods_id(self):
  638. '''当订单行的商品变化时,带出商品上的单位、默认仓库、价格、税率'''
  639. if self.goods_id:
  640. self.uom_id = self.goods_id.uom_id
  641. self.price = self.goods_id.price
  642. self.tax_rate = self.goods_id.get_tax_rate(self.goods_id, self.order_id.partner_id, 'sell')
  643. @api.onchange('quantity', 'price_taxed', 'discount_rate')
  644. def onchange_discount_rate(self):
  645. '''当数量、单价或优惠率发生变化时,优惠金额发生变化'''
  646. _logger.info('数量、含税价、折扣率发生变化')
  647. self.price = self.price_taxed / (1 + self.tax_rate * 0.01)
  648. self.discount_amount = self.quantity * self.price \
  649. * self.discount_rate * 0.01
  650. @api.constrains('tax_rate')
  651. def _check_tax_rate(selfs):
  652. for self in selfs:
  653. if self.tax_rate > 100:
  654. raise UserError('税率不能输入超过100的数!\n输入税率:%s' % self.tax_rate)
  655. if self.tax_rate < 0:
  656. raise UserError('税率不能输入负数\n 输入税率:%s' % self.tax_rate)
  657. class ApproveMultiSellOrder(models.TransientModel):
  658. _name = "approve.multi.sell.order"
  659. _description = '批量确认销售订单'
  660. def set_default_note(self):
  661. """
  662. 设置默认值, 用来确认要批量确认的订单
  663. """
  664. context = self.env.context
  665. order_names = [order.name for order in self.env['sell.order'].browse(context.get('active_ids'))]
  666. return '-'.join(order_names)
  667. note = fields.Char('本次处理销售订单', default=set_default_note, readonly=True)
  668. @api.model
  669. def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
  670. """ 根据内容判断 报出错误 """
  671. res = super(ApproveMultiSellOrder, self).fields_view_get(view_id, view_type, toolbar=toolbar, submenu=False)
  672. orders = self.env['sell.order'].browse(self.env.context.get('active_ids'))
  673. done_lists = ''
  674. for order in orders:
  675. if order.state == 'done':
  676. done_lists += order.name
  677. if done_lists:
  678. raise UserError('销售订单 ' + done_lists + ' 已确认!')
  679. return res
  680. def approve_sell_order(self):
  681. """ 确认销售订单 """
  682. for order in self.env['sell.order'].search([('id', 'in', self.env.context.get('active_ids'))]):
  683. order.sell_order_done()
上海开阖软件有限公司 沪ICP备12045867号-1