GoodERP
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

414 lines
16KB

  1. from .utils import inherits, inherits_after, create_name, create_origin
  2. from odoo import models, fields, api
  3. from odoo.exceptions import UserError
  4. import logging
  5. _logger = logging.getLogger(__name__)
  6. class WhOut(models.Model):
  7. _name = 'wh.out'
  8. _description = '其他出库单'
  9. _inherit = ['mail.thread', 'barcodes.barcode_events_mixin']
  10. _order = 'date DESC, id DESC'
  11. _inherits = {
  12. 'wh.move': 'move_id',
  13. }
  14. TYPE_SELECTION = [
  15. ('inventory', '盘亏'),
  16. ('others', '其他出库'),
  17. ('cancel', '已作废')]
  18. move_id = fields.Many2one('wh.move', '移库单', required=True, index=True, ondelete='cascade',
  19. help='其他出库单对应的移库单')
  20. type = fields.Selection(TYPE_SELECTION, '业务类别', default='others',
  21. help='类别: 盘亏,其他出库')
  22. amount_total = fields.Float(compute='_get_amount_total', string='合计成本金额',
  23. store=True, readonly=True, digits='Amount',
  24. help='该出库单的出库金额总和')
  25. voucher_id = fields.Many2one('voucher', '出库凭证',
  26. readonly=True,
  27. help='该出库单的后生成的出库凭证')
  28. @inherits_after()
  29. def approve_order(self):
  30. for order in self:
  31. if order.state == 'done':
  32. raise UserError('请不要重复出库')
  33. if self.env.user.company_id.is_enable_negative_stock: # 其他出库单检查负库存
  34. result_vals = self.env['wh.move'].create_zero_wh_in(self, self._name)
  35. if result_vals:
  36. return result_vals
  37. voucher = order.create_voucher()
  38. order.write({
  39. 'voucher_id': voucher and voucher[0] and voucher[0].id,
  40. 'state': 'done',
  41. })
  42. return True
  43. @inherits()
  44. def cancel_approved_order(self):
  45. for order in self:
  46. if order.state == 'draft':
  47. raise UserError('请不要重复撤销 %s' % self._description)
  48. order.delete_voucher()
  49. order.state = 'draft'
  50. return True
  51. @inherits()
  52. def unlink(self):
  53. for order in self:
  54. return order.move_id.unlink()
  55. @api.depends('line_out_ids.cost')
  56. def _get_amount_total(self):
  57. for wo in self:
  58. wo.amount_total = sum(line.cost for line in wo.line_out_ids)
  59. def get_move_origin(self, vals):
  60. return self._name + '.' + vals.get('type')
  61. @api.model
  62. @create_name
  63. @create_origin
  64. def create(self, vals):
  65. return super(WhOut, self).create(vals)
  66. @api.onchange('type')
  67. def onchange_type(self):
  68. self.warehouse_dest_id = self.env['warehouse'].get_warehouse_by_type(
  69. self.type)
  70. def goods_inventory(self, vals):
  71. """
  72. 审核时若仓库中商品不足,则产生补货向导生成其他入库单并审核。
  73. :param vals: 创建其他入库单需要的字段及取值信息构成的字典
  74. :return:
  75. """
  76. auto_in = self.env['wh.in'].create(vals)
  77. self.with_context({'wh_in_line_ids': [line.id for line in
  78. auto_in.line_in_ids]}).approve_order()
  79. def create_voucher(self):
  80. '''
  81. 其他出库单生成出库凭证
  82. 借:如果出库类型为盘亏,取科目 1901 待处理财产损益;如果为其他,取核算类别的会计科目
  83. 贷:库存商品(商品分类上会计科目)
  84. '''
  85. voucher = self.env['voucher'].create({'date': self.date
  86. })
  87. credit_sum = 0 # 贷方之和
  88. for line in self.line_out_ids:
  89. if line.cost: # 贷方行(多行)
  90. self.env['voucher.line'].create({
  91. 'name': '%s %s' % (self.name, self.note or ''),
  92. 'account_id': line.goods_id.category_id.account_id.id,
  93. 'credit': line.cost,
  94. 'voucher_id': voucher.id,
  95. 'goods_id': line.goods_id.id,
  96. 'goods_qty': line.goods_qty,
  97. })
  98. credit_sum += line.cost
  99. account = self.type == 'inventory' \
  100. and self.env.ref('finance.small_business_chart1901') \
  101. or self.finance_category_id.account_id
  102. if credit_sum: # 借方行(汇总一行)
  103. self.env['voucher.line'].create({
  104. 'name': '%s %s' % (self.name, self.note or ''),
  105. 'account_id': account.id,
  106. 'auxiliary_id': self.auxiliary_id.id,
  107. 'debit': credit_sum,
  108. 'voucher_id': voucher.id,
  109. })
  110. if len(voucher.line_ids) > 0:
  111. voucher.voucher_done()
  112. return voucher
  113. else:
  114. voucher.unlink()
  115. def delete_voucher(self):
  116. # 反审核其他出库单时删除对应的出库凭证
  117. voucher = self.voucher_id
  118. if voucher.state == 'done':
  119. voucher.voucher_draft()
  120. voucher.unlink()
  121. def on_barcode_scanned(self, barcode):
  122. self.move_id.scan_barcode(self._name, barcode, self._origin.id)
  123. def action_batch_split(self):
  124. """其他出库单 按批次先进先出原则 批量拆分各批次商品 行"""
  125. for line_out in self.line_out_ids:
  126. res = line_out.env['wh.move.line'].search([ # 按商品去找出这个商品的所有批次数
  127. ('goods_id', '=', line_out.goods_id.id),
  128. ('state', '=', 'done'),
  129. ('lot', '!=', False),
  130. ('qty_remaining', '>', 0),
  131. ('warehouse_dest_id', '=', self.warehouse_id.id),
  132. ], order='lot asc, qty_remaining asc')
  133. if len(res):
  134. """
  135. 第一步:先把当前大于1的商品数量记下来
  136. 第二步:把这个商品行删除掉
  137. 第三步:按批次去拆分第一步记下来的数量
  138. """
  139. # 第一步:先把当前大于1的商品数量记下来
  140. wh_obj = line_out.env['wh.move.line'].search([
  141. ('goods_id', '=', line_out.goods_id.id),
  142. ('move_id', '=', line_out.move_id.id),
  143. ])
  144. v = sum(lin.goods_qty for lin in wh_obj)
  145. # 第二步:把这个商品行删除掉
  146. sql = """delete from wh_move_line where goods_id = '{}' and move_id = '{}'
  147. """.format(line_out.goods_id.id, line_out.move_id.id)
  148. self._cr.execute(sql)
  149. # 第三步:按批次去拆分第一步记下来的数量
  150. for line in res:
  151. if v <= 0:
  152. continue
  153. if v >= line.qty_remaining:
  154. vals = {
  155. 'move_id': line_out.move_id.id,
  156. 'goods_id': line_out.goods_id.id,
  157. 'lot': line.lot,
  158. 'lot_id': line.id,
  159. 'goods_qty': line.qty_remaining,
  160. 'type': 'out',
  161. 'cost_unit': line.cost_unit,
  162. 'cost': line.cost,
  163. 'expiration_date': line.expiration_date,
  164. }
  165. line_out.env['wh.move.line'].create(vals)
  166. v -= line.qty_remaining
  167. else:
  168. vals = {
  169. 'move_id': line_out.move_id.id,
  170. 'goods_id': line_out.goods_id.id,
  171. 'lot': line.lot,
  172. 'lot_id': line.id,
  173. 'goods_qty': v,
  174. 'type': 'out',
  175. 'cost_unit': line.cost_unit,
  176. 'cost': line.cost,
  177. 'expiration_date': line.expiration_date,
  178. }
  179. line_out.env['wh.move.line'].create(vals)
  180. v -= line.qty_remaining
  181. class WhIn(models.Model):
  182. _name = 'wh.in'
  183. _description = '其他入库单'
  184. _inherit = ['mail.thread']
  185. _order = 'date DESC, id DESC'
  186. _inherits = {
  187. 'wh.move': 'move_id',
  188. }
  189. TYPE_SELECTION = [
  190. ('inventory', '盘盈'),
  191. ('others', '其他入库'),
  192. ]
  193. move_id = fields.Many2one('wh.move', '移库单', required=True, index=True, ondelete='cascade',
  194. help='其他入库单对应的移库单')
  195. type = fields.Selection(TYPE_SELECTION, '业务类别', default='others',
  196. help='类别: 盘盈,其他入库,初始')
  197. amount_total = fields.Float(compute='_get_amount_total', string='合计成本金额',
  198. store=True, readonly=True, digits='Amount',
  199. help='该入库单的入库金额总和')
  200. voucher_id = fields.Many2one('voucher', '入库凭证',
  201. readonly=True,
  202. help='该入库单确认后生成的入库凭证')
  203. is_init = fields.Boolean('初始化单')
  204. @inherits()
  205. def approve_order(self):
  206. for order in self:
  207. if order.state == 'done':
  208. raise UserError('请不要重复入库')
  209. voucher = order.create_voucher()
  210. order.write({
  211. 'voucher_id': voucher and voucher[0] and voucher[0].id,
  212. 'state': 'done',
  213. })
  214. return True
  215. @inherits()
  216. def cancel_approved_order(self):
  217. for order in self:
  218. if order.state == 'draft':
  219. raise UserError('请不要重复撤销 %s' % self._description)
  220. order.delete_voucher()
  221. order.state = 'draft'
  222. return True
  223. @inherits()
  224. def unlink(self):
  225. for order in self:
  226. return order.move_id.unlink()
  227. @api.depends('line_in_ids.cost')
  228. def _get_amount_total(self):
  229. self.amount_total = sum(line.cost for line in self.line_in_ids)
  230. def get_move_origin(self, vals):
  231. return self._name + '.' + vals.get('type')
  232. @api.model
  233. @create_name
  234. @create_origin
  235. def create(self, vals):
  236. return super(WhIn, self).create(vals)
  237. @api.onchange('type')
  238. def onchange_type(self):
  239. self.warehouse_id = self.env['warehouse'].get_warehouse_by_type(
  240. self.type).id
  241. def create_voucher(self):
  242. # 入库单生成入库凭证
  243. '''
  244. 借:商品分类对应的会计科目 一般是库存商品
  245. 贷:如果入库类型为盘盈,取科目 1901 待处理财产损益(暂时写死)
  246. 如果入库类型为其他,取收发类别的会计科目
  247. '''
  248. # 初始化单的话,先找是否有初始化凭证,没有则新建一个
  249. if self.is_init:
  250. vouch_id = self.env['voucher'].search([('is_init', '=', True)])
  251. if not vouch_id:
  252. vouch_id = self.env['voucher'].create({'date': self.date,
  253. 'is_init': True
  254. })
  255. else:
  256. vouch_id = self.env['voucher'].create({'date': self.date
  257. })
  258. debit_sum = 0
  259. for line in self.line_in_ids:
  260. init_obj = self.is_init and 'init_warehouse - %s' % (self.id) or ''
  261. if line.cost:
  262. self.env['voucher.line'].create({
  263. 'name': '%s %s' % (self.name, self.note or ''),
  264. 'account_id': line.goods_id.category_id.account_id.id,
  265. 'debit': line.cost,
  266. 'voucher_id': vouch_id.id,
  267. 'goods_id': line.goods_id.id,
  268. 'goods_qty': line.goods_qty,
  269. 'init_obj': init_obj,
  270. })
  271. debit_sum += line.cost
  272. # 贷方科目: 如果是盘盈则取主营业务成本,否则取收发类别上的科目
  273. account = self.type == 'inventory' \
  274. and self.env.ref('finance.small_business_chart1901') \
  275. or self.finance_category_id.account_id
  276. if not self.is_init:
  277. if debit_sum:
  278. self.env['voucher.line'].create({
  279. 'name': '%s %s' % (self.name, self.note or ''),
  280. 'account_id': account.id,
  281. 'auxiliary_id': self.auxiliary_id.id,
  282. 'credit': debit_sum,
  283. 'voucher_id': vouch_id.id,
  284. })
  285. if not self.is_init:
  286. if len(vouch_id.line_ids) > 0:
  287. vouch_id.voucher_done()
  288. return vouch_id
  289. else:
  290. vouch_id.unlink()
  291. else:
  292. return vouch_id
  293. def delete_voucher(self):
  294. # 反审核入库单时删除对应的入库凭证
  295. if self.voucher_id:
  296. if self.voucher_id.state == 'done':
  297. self.voucher_id.voucher_draft()
  298. voucher = self.voucher_id
  299. # 始初化单反审核只删除明细行
  300. if self.is_init:
  301. vouch_obj = self.env['voucher'].search(
  302. [('id', '=', voucher.id)])
  303. vouch_obj_lines = self.env['voucher.line'].search([
  304. ('voucher_id', '=', vouch_obj.id),
  305. ('goods_id', 'in', [
  306. line.goods_id.id for line in self.line_in_ids]),
  307. ('init_obj', '=', 'init_warehouse - %s' % (self.id)), ])
  308. for vouch_obj_line in vouch_obj_lines:
  309. vouch_obj_line.unlink()
  310. else:
  311. voucher.unlink()
  312. class WhInternal(models.Model):
  313. _name = 'wh.internal'
  314. _description = '内部调拨单'
  315. _inherit = ['mail.thread']
  316. _order = 'date DESC, id DESC'
  317. _inherits = {
  318. 'wh.move': 'move_id',
  319. }
  320. move_id = fields.Many2one('wh.move', '移库单', required=True, index=True, ondelete='cascade',
  321. help='调拨单对应的移库单')
  322. amount_total = fields.Float(compute='_get_amount_total', string='合计成本金额',
  323. store=True, readonly=True, digits='Amount',
  324. help='该调拨单的出库金额总和')
  325. def goods_inventory(self, vals):
  326. """
  327. 审核时若仓库中商品不足,则产生补货向导生成其他入库单并审核。
  328. :param vals: 创建其他入库单需要的字段及取值信息构成的字典
  329. :return:
  330. """
  331. auto_in = self.env['wh.in'].create(vals)
  332. self.with_context({'wh_in_line_ids': [line.id for line in
  333. auto_in.line_in_ids]}).approve_order()
  334. @inherits_after()
  335. def approve_order(self):
  336. for order in self:
  337. if order.state == 'done':
  338. raise UserError('请不要重复入库')
  339. if self.env.user.company_id.is_enable_negative_stock: # 移库单检查负库存
  340. result_vals = self.env['wh.move'].create_zero_wh_in(
  341. self, self._name)
  342. if result_vals:
  343. return result_vals
  344. order.state = 'done'
  345. return True
  346. @inherits()
  347. def cancel_approved_order(self):
  348. for order in self:
  349. if order.state == 'draft':
  350. raise UserError('请不要重复撤销 %s' % self._description)
  351. order.state = 'draft'
  352. return True
  353. @inherits()
  354. def unlink(self):
  355. for order in self:
  356. return order.move_id.unlink()
  357. @api.depends('line_out_ids.cost')
  358. def _get_amount_total(self):
  359. self.amount_total = sum(line.cost for line in self.line_out_ids)
  360. @api.model
  361. @create_name
  362. @create_origin
  363. def create(self, vals):
  364. return super(WhInternal, self).create(vals)
上海开阖软件有限公司 沪ICP备12045867号-1