GoodERP
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

386 Zeilen
19KB

  1. # -*- coding: utf-8 -*-
  2. from odoo import _
  3. from odoo import models, fields, api
  4. from datetime import datetime, timedelta
  5. class ReportStockReceiptDelivery(models.TransientModel):
  6. _name = 'report.stock.receipt.delivery'
  7. _description = '商品收发汇总表'
  8. goods_class = fields.Char('商品类别')
  9. code = fields.Char('商品编号')
  10. goods = fields.Char('商品名称')
  11. uom = fields.Char('单位')
  12. attribute = fields.Char('属性')
  13. id_lists = fields.Text('库存调拨id列表')
  14. warehouse = fields.Char('仓库')
  15. origin = fields.Char('业务类型')
  16. goods_qty_begin = fields.Float(
  17. '期初数量', digits='Quantity')
  18. cost_begin = fields.Float(
  19. '期初成本', digits='Amount')
  20. goods_qty_end = fields.Float(
  21. '期末数量', digits='Quantity')
  22. cost_end = fields.Float(
  23. '期末成本', digits='Amount')
  24. goods_qty_out = fields.Float(
  25. '出库数量', digits='Quantity')
  26. cost_out = fields.Float(
  27. '出库总成本', digits='Amount')
  28. goods_qty_in = fields.Float(
  29. '入库数量', digits='Quantity')
  30. cost_in = fields.Float(
  31. '入库成本', digits='Amount')
  32. # 入库数据
  33. goods_buy_receipt = fields.Float(
  34. '采购入库', digits='Quantity')
  35. cost_buy_receipt = fields.Float(
  36. '采购成本', digits='Amount')
  37. goods_in_mrp_in = fields.Float(
  38. '生产入库', digits='Quantity')
  39. cost_in_mrp_in = fields.Float(
  40. '生产成本', digits='Amount')
  41. goods_in_others = fields.Float(
  42. '其他入库', digits='Quantity')
  43. cost_in_others = fields.Float(
  44. '其他入库成本', digits='Amount')
  45. goods_in_inventory = fields.Float(
  46. '盘盈入库', digits='Quantity')
  47. cost_in_inventory = fields.Float(
  48. '盘盈成本', digits='Amount')
  49. goods_delivery_return = fields.Float(
  50. '销售退货', digits='Quantity')
  51. cost_delivery_return = fields.Float(
  52. '销售退货成本', digits='Amount')
  53. goods_assembly_in = fields.Float(
  54. '组装入库', digits='Quantity')
  55. cost_assembly_in = fields.Float(
  56. '组装入库成本', digits='Amount')
  57. goods_internal_in = fields.Float(
  58. '调拨入库', digits='Quantity')
  59. cost_internal_in = fields.Float(
  60. '调拨入库成本', digits='Amount')
  61. # 出库数据
  62. goods_delivery_sell = fields.Float(
  63. '销售出库', digits='Quantity')
  64. cost_delivery_sell = fields.Float(
  65. '销售出库成本', digits='Amount')
  66. goods_out_others = fields.Float(
  67. '其他出库', digits='Quantity')
  68. cost_out_others = fields.Float(
  69. '其他出库成本', digits='Amount')
  70. goods_out_inventory = fields.Float(
  71. '盘亏出库', digits='Quantity')
  72. cost_out_inventory = fields.Float(
  73. '盘亏成本', digits='Amount')
  74. goods_receipt_return = fields.Float(
  75. '采购退货', digits='Quantity')
  76. cost_receipt_return = fields.Float(
  77. '采购退货成本', digits='Amount')
  78. goods_assembly_out = fields.Float(
  79. '组装出库', digits='Quantity')
  80. cost_assembly_out = fields.Float(
  81. '组装出库成本', digits='Amount')
  82. goods_internal_out = fields.Float(
  83. '调拨出库', digits='Quantity')
  84. cost_internal_out = fields.Float(
  85. '调拨出库成本', digits='Amount')
  86. def select_sql(self, sql_type='out', sql_origin=''):
  87. return '''
  88. SELECT goods.name as goods,
  89. gc.name as goods_class,
  90. goods.code as code,
  91. att.name as attribute,
  92. array_agg(line.id) as id_lists,
  93. uom.name as uom,
  94. wh.name as warehouse,
  95. case
  96. when wm.origin = 'buy.receipt.buy' then '采购入库'
  97. when wm.origin = 'wh.in.others' then '其他入库'
  98. when wm.origin = 'wh.in.inventory' then '盘盈'
  99. when wm.origin = 'sell.delivery.return' then '销售退货'
  100. when wm.origin = 'mrp.plm.in' then '生产入库'
  101. when wm.origin = 'sell.delivery.sell' then '销售出库'
  102. when wm.origin = 'wh.out.others' then '其他出库'
  103. when wm.origin = 'wh.out.inventory' then '盘亏'
  104. when wm.origin = 'buy.receipt.return' then '采购退货'
  105. when wm.origin = 'wh.internal' then '%s'
  106. when wm.origin = 'wh.assembly' and line.type = 'out' then '组装单子件'
  107. when wm.origin = 'wh.assembly' and line.type = 'in' then '组装单组合件'
  108. when wm.origin = 'outsource' and line.type = 'out' then '委外单子件'
  109. when wm.origin = 'outsource' and line.type = 'in' then '委外单组合件'
  110. end as origin,
  111. att.name as attribute,
  112. array_agg(line.id order by line.id) as id_lists,
  113. wh.name as warehouse,
  114. sum(case when
  115. line.date < '{date_start}' THEN line.goods_qty ELSE 0 END)
  116. as goods_qty_begin,
  117. sum(case when
  118. line.date < '{date_start}' THEN line.cost ELSE 0 END)
  119. as cost_begin,
  120. sum(case when
  121. line.date <= '{date_end}' THEN line.goods_qty ELSE 0 END)
  122. as goods_qty_end,
  123. sum(case when
  124. line.date <= '{date_end}' THEN line.cost ELSE 0 END)
  125. as cost_end,
  126. sum(case when
  127. line.date <= '{date_end}' AND line.date >= '{date_start}'
  128. THEN
  129. line.goods_qty ELSE 0 END)
  130. as goods_qty,
  131. sum(case when
  132. line.date <= '{date_end}' AND line.date >= '{date_start}'
  133. THEN
  134. line.cost ELSE 0 END)
  135. as cost
  136. ''' % ('调拨出库' if sql_origin == 'internal_in' else '调拨入库')
  137. def from_sql(self, sql_type='out', sql_origin=''):
  138. return '''
  139. FROM wh_move_line line
  140. inner join wh_move wm on wm.id = line.move_id
  141. inner join goods goods ON line.goods_id = goods.id
  142. left join goods_class gc on gc.id = goods.goods_class_id
  143. LEFT JOIN attribute att ON line.attribute_id = att.id
  144. LEFT JOIN uom uom ON line.uom_id = uom.id
  145. LEFT JOIN warehouse wh ON line.%s = wh.id
  146. ''' % ((sql_type == 'out' or (sql_type == 'internal' and sql_origin == 'internal_in')) and 'warehouse_id'
  147. or 'warehouse_dest_id')
  148. def where_sql(self, sql_type='out', sql_origin=''):
  149. extra = ''
  150. if self.env.context.get('warehouse_id'):
  151. extra += 'AND wh.id = {warehouse_id}'
  152. if self.env.context.get('goods_id'):
  153. extra += 'AND goods.id = {goods_id}'
  154. return '''
  155. WHERE line.state = 'done'
  156. AND wh.type = 'stock'
  157. AND wh.active = true
  158. AND line.date <= '{date_end}'
  159. %s
  160. ''' % extra
  161. def group_sql(self, sql_type='out', sql_origin=''):
  162. return '''
  163. GROUP BY gc.name, goods.id, wm.origin, line.type, goods.code,
  164. att.name, uom.name, wh.name, line.warehouse_id, line.type
  165. '''
  166. def order_sql(self, sql_type='out', sql_origin=''):
  167. return '''
  168. ORDER BY goods.id, wh.name
  169. '''
  170. def make_hashable(self, item):
  171. """将可能包含字典的元素转换为可哈希形式"""
  172. if isinstance(item, dict):
  173. # 转换为元组的元组,确保顺序一致
  174. return tuple(sorted((k, self.make_hashable(v)) for k, v in item.items()))
  175. elif isinstance(item, list):
  176. return tuple(self.make_hashable(x) for x in item)
  177. else:
  178. return item
  179. def get_record_key(self, record, sql_type='out'):
  180. return (
  181. self.make_hashable(record.get('goods')),
  182. self.make_hashable(record.get('uom')),
  183. self.make_hashable(record.get('warehouse')),
  184. self.make_hashable(record.get('attribute')),
  185. self.make_hashable(record.get('code')),
  186. self.make_hashable(record.get('goods_class'))
  187. )
  188. def unzip_record_key(self, key):
  189. return {
  190. 'goods': key[0],
  191. 'uom': key[1],
  192. 'warehouse': key[2],
  193. 'attribute': key[3],
  194. 'code': key[4],
  195. 'goods_class': key[5]
  196. }
  197. def get_default_value_by_record(self, record, sql_type='out', sql_origin=''):
  198. return {
  199. 'id': record.get('id'),
  200. }
  201. def update_record_value(self, value, record, sql_type='out', sql_origin=''):
  202. tag = sql_type == 'out' and -1 or 1
  203. value.update({
  204. # 调拨入库
  205. 'goods_internal_in': value.get('goods_internal_in', 0) + (
  206. sql_type == 'internal' and sql_origin == '调拨入库' and record.get('goods_qty', 0) or 0),
  207. 'cost_internal_in': value.get('cost_internal_in', 0) + (
  208. sql_type == 'internal' and sql_origin == '调拨入库' and record.get('cost', 0) or 0),
  209. # 调拨出库
  210. 'goods_internal_out': value.get('goods_internal_out', 0) + (
  211. sql_type == 'internal' and sql_origin == '调拨出库' and record.get('goods_qty', 0) or 0),
  212. 'cost_internal_out': value.get('cost_internal_out', 0) + (
  213. sql_type == 'internal' and sql_origin == '调拨出库' and record.get('cost', 0) or 0),
  214. # =============================期初数量=============================
  215. 'goods_qty_begin': value.get('goods_qty_begin', 0) + (
  216. sql_type != 'internal' and tag * record.get('goods_qty_begin', 0)),
  217. 'cost_begin': value.get('cost_begin', 0) + (
  218. sql_type != 'internal' and tag * record.get('cost_begin', 0)),
  219. 'goods_qty_end': value.get('goods_qty_end', 0) + (
  220. sql_type != 'internal' and tag * record.get('goods_qty_end', 0)),
  221. 'cost_end': value.get('cost_end', 0) + (
  222. sql_type != 'internal' and tag * record.get('cost_end', 0)),
  223. # =============================一批入库数据=============================
  224. # 采购入库
  225. 'goods_buy_receipt': value.get('goods_buy_receipt', 0) +
  226. (sql_type == 'in' and sql_origin == '采购入库' and record.get('goods_qty', 0) or 0),
  227. 'cost_buy_receipt': value.get('cost_buy_receipt', 0) +
  228. (sql_type == 'in' and sql_origin == '采购入库' and record.get('cost', 0) or 0),
  229. # 生产入库
  230. 'goods_in_mrp_in': value.get('goods_in_mrp_in', 0) +
  231. (sql_type == 'in' and sql_origin == '生产入库' and record.get('goods_qty', 0) or 0),
  232. 'cost_in_mrp_in': value.get('cost_in_mrp_in', 0) +
  233. (sql_type == 'in' and sql_origin == '生产入库' and record.get('cost', 0) or 0),
  234. # 其他入库
  235. 'goods_in_others': value.get('goods_in_others', 0) +
  236. (sql_type == 'in' and sql_origin == '其他入库' and record.get('goods_qty', 0) or 0),
  237. 'cost_in_others': value.get('cost_in_others', 0) +
  238. (sql_type == 'in' and sql_origin == '其他入库' and record.get('cost', 0) or 0),
  239. # 盘盈入库
  240. 'goods_in_inventory': value.get('goods_in_inventory', 0) +
  241. (sql_type == 'in' and sql_origin == '盘盈' and record.get('goods_qty', 0) or 0),
  242. 'cost_in_inventory': value.get('cost_in_inventory', 0) +
  243. (sql_type == 'in' and sql_origin == '盘盈' and record.get('cost', 0) or 0),
  244. # 销售退货
  245. 'goods_delivery_return': value.get('goods_delivery_return', 0) +
  246. (sql_type == 'in' and sql_origin == '销售退货' and record.get('goods_qty', 0) or 0),
  247. 'cost_delivery_return': value.get('cost_delivery_return', 0) +
  248. (sql_type == 'in' and sql_origin == '销售退货' and record.get('cost', 0) or 0),
  249. # 组装单组合件(组装入库)
  250. 'goods_assembly_in': value.get('goods_assembly_in', 0) +
  251. (sql_type == 'in' and sql_origin == '组装单组合件' and record.get('goods_qty',
  252. 0) or 0),
  253. 'cost_assembly_in': value.get('cost_assembly_in', 0) +
  254. (sql_type == 'in' and sql_origin == '组装单组合件' and record.get('cost', 0) or 0),
  255. # =============================一批出库数据=============================
  256. # 销售出库
  257. 'goods_delivery_sell': value.get('goods_delivery_sell', 0) +
  258. (sql_type == 'out' and sql_origin == '销售出库' and record.get('goods_qty', 0) or 0),
  259. 'cost_delivery_sell': value.get('cost_delivery_sell', 0) +
  260. (sql_type == 'out' and sql_origin == '销售出库' and record.get('cost', 0) or 0),
  261. # 其他出库
  262. 'goods_out_others': value.get('goods_out_others', 0) +
  263. (sql_type == 'out' and sql_origin == '其他出库' and record.get('goods_qty', 0) or 0),
  264. 'cost_out_others': value.get('cost_out_others', 0) +
  265. (sql_type == 'out' and sql_origin == '其他出库' and record.get('cost', 0) or 0),
  266. # 盘亏出库
  267. 'goods_out_inventory': value.get('goods_out_inventory', 0) +
  268. (sql_type == 'out' and sql_origin == '盘亏' and record.get('goods_qty', 0) or 0),
  269. 'cost_out_inventory': value.get('cost_out_inventory', 0) +
  270. (sql_type == 'out' and sql_origin == '盘亏' and record.get('cost', 0) or 0),
  271. # 采购退货
  272. 'goods_receipt_return': value.get('goods_receipt_return', 0) +
  273. (sql_type == 'out' and sql_origin == '采购退货' and record.get('goods_qty',
  274. 0) or 0),
  275. 'cost_receipt_return': value.get('cost_receipt_return', 0) +
  276. (sql_type == 'out' and sql_origin == '采购退货' and record.get('cost', 0) or 0),
  277. # 组装单子件(组装出库)
  278. 'goods_assembly_out': value.get('goods_assembly_out', 0) +
  279. (sql_type == 'out' and sql_origin == '组装单子件' and record.get('goods_qty',
  280. 0) or 0),
  281. 'cost_assembly_out': value.get('cost_assembly_out', 0) +
  282. (sql_type == 'out' and sql_origin == '组装单子件' and record.get('cost', 0) or 0),
  283. # =============================期末数量=============================
  284. 'goods_qty_in': value.get('goods_qty_in', 0) + (sql_type == 'in' and record.get('goods_qty', 0) or 0),
  285. 'cost_in': value.get('cost_in', 0) + (sql_type == 'in' and record.get('cost', 0) or 0),
  286. 'goods_qty_out': value.get('goods_qty_out', 0) + (sql_type == 'out' and record.get('goods_qty', 0) or 0),
  287. 'cost_out': value.get('cost_out', 0) + (sql_type == 'out' and record.get('cost', 0) or 0),
  288. 'id_lists': value.get('id_lists', []) + record.get('id_lists', []),
  289. 'origin': sql_origin,
  290. })
  291. def compute_history_stock_by_collect(self, res, records, sql_type='out', sql_origin=''):
  292. for record in records:
  293. record_key = self.get_record_key(record, sql_type=sql_type)
  294. if not res.get(record_key):
  295. res[record_key] = self.get_default_value_by_record(
  296. record, sql_type=sql_type, sql_origin=record['origin'])
  297. self.update_record_value(
  298. res[record_key], record, sql_type=sql_type, sql_origin=record['origin'])
  299. def execute_sql(self, sql_type='out', sql_origin='', wizard_id=False):
  300. context = {
  301. 'date_start': wizard_id.date_start or '',
  302. 'date_end': wizard_id.date_end or '',
  303. 'warehouse_id': wizard_id.warehouse_id and wizard_id.warehouse_id[0].id or '',
  304. 'goods_id': wizard_id.goods_id and wizard_id.goods_id[0].id or '',
  305. }
  306. for key, value in list(context.items()):
  307. if key == "date_end":
  308. continue
  309. if key == "date_start":
  310. continue
  311. if isinstance(context[key], str):
  312. context[key] = value.encode('utf-8')
  313. self.env.context = dict(self.env.context, **context)
  314. search_sql = (self.select_sql(sql_type, sql_origin) + self.from_sql(sql_type, sql_origin)
  315. + self.where_sql(sql_type, sql_origin) + self.group_sql(sql_type, sql_origin)
  316. + self.order_sql(sql_type, sql_origin)).format(**context)
  317. self.env.cr.execute(search_sql)
  318. return self.env.cr.dictfetchall()
  319. def collect_data_by_sql(self, wizard_id=None):
  320. out_collection = self.execute_sql(sql_type='out', sql_origin='', wizard_id=wizard_id)
  321. in_collection = self.execute_sql(sql_type='in', sql_origin='', wizard_id=wizard_id)
  322. internal_in_collection = self.execute_sql(sql_type='internal', sql_origin='internal_in', wizard_id=wizard_id)
  323. internal_out_collection = self.execute_sql(sql_type='internal', sql_origin='internal_out', wizard_id=wizard_id)
  324. res = {}
  325. self.compute_history_stock_by_collect(res, out_collection, sql_type='out')
  326. self.compute_history_stock_by_collect(res, in_collection, sql_type='in')
  327. self.compute_history_stock_by_collect(res, internal_out_collection, sql_type='internal')
  328. self.compute_history_stock_by_collect(res, internal_in_collection, sql_type='internal')
  329. result = []
  330. for key, value in res.items():
  331. value.update(self.unzip_record_key(key))
  332. result.append(value)
  333. return result
  334. def find_source_move_line(self):
  335. # 查看库存调拨明细
  336. move_line_ids = []
  337. # 获得'report.stock.receipt.delivery'记录集
  338. date_start = self.env.context.get('date_start')
  339. date_end = self.env.context.get('date_end')
  340. domain_dict = [
  341. ('date', '>=', date_start),
  342. ('date', '<=', date_end),
  343. ('id', 'in', [int(mid) for mid in self.id_lists[1:-1].split(',')]),
  344. ]
  345. # move_line_ids = self.env['wh.move.line'].search(domain_dict).ids
  346. view = self.env.ref('warehouse.wh_move_line_list')
  347. return {
  348. 'name': (_('库存调拨') + str(date_start) +
  349. '~' + str(date_end) +
  350. '~' + self.goods),
  351. 'view_mode': 'list',
  352. 'views': [(view.id, 'list')],
  353. 'res_model': 'wh.move.line',
  354. 'type': 'ir.actions.act_window',
  355. 'domain': domain_dict,
  356. }
上海开阖软件有限公司 沪ICP备12045867号-1