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.

295 Zeilen
12KB

  1. # Copyright 2016 上海开阖软件有限公司 (http://www.osbzr.com)
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. from odoo.exceptions import UserError
  4. from odoo import fields, models, api
  5. class MoneyInvoice(models.Model):
  6. _name = 'money.invoice'
  7. _description = '结算单'
  8. _order = 'date DESC'
  9. @api.model
  10. def _get_category_id(self):
  11. cate_type = self.env.context.get('type')
  12. if cate_type:
  13. return self.env['core.category'].search(
  14. [('type', '=', cate_type)])[0]
  15. return False
  16. def name_get(self):
  17. '''在many2one字段里有order则显示单号否则显示名称_票号'''
  18. res = []
  19. for invoice in self:
  20. if self.env.context.get('order'):
  21. res.append((invoice.id, invoice.name))
  22. else:
  23. res.append(
  24. (invoice.id, invoice.bill_number
  25. and invoice.bill_number or invoice.name))
  26. return res
  27. @api.depends('bill_number', 'name')
  28. def _compute_display_name(self):
  29. '''在many2one字段里有order则显示单号否则显示名称_票号, 注意原来的 name_get 被name_search使用, 不能删除'''
  30. for record in self:
  31. if self.env.context.get('order'):
  32. record.display_name = record.name
  33. else:
  34. record.display_name = record.bill_number and (record.name + '_' + record.bill_number) or record.name
  35. @api.depends('date_due', 'to_reconcile')
  36. def compute_overdue(self):
  37. """
  38. 计算逾期天数: 当前日期 - 到期日,< 0则显示为0;如果逾期金额为0则逾期天数也为0
  39. 计算逾期金额: 逾期时等于未核销金额,否则为0
  40. :return: 逾期天数
  41. """
  42. for s in self:
  43. # 只计算未核销的
  44. s.overdue_days = 0
  45. s.overdue_amount = 0
  46. if s.to_reconcile and s.state == 'done':
  47. d1 = fields.Date.context_today(s)
  48. d2 = s.date_due or d1
  49. day = (d1 - d2).days
  50. if day > 0:
  51. s.overdue_days = day
  52. s.overdue_amount = s.to_reconcile
  53. @api.depends('reconciled')
  54. def _get_sell_amount_state(self):
  55. for mi in self:
  56. if mi.reconciled:
  57. mi.get_amount_date = mi.write_date
  58. state = fields.Selection([
  59. ('draft', '草稿'),
  60. ('done', '完成')
  61. ], string='状态',
  62. default='draft', copy=False, index=True,
  63. help='结算单状态标识,新建时状态为草稿;确认后状态为完成')
  64. partner_id = fields.Many2one('partner', string='往来单位',
  65. required=True,
  66. ondelete='restrict',
  67. help='该单据对应的业务伙伴')
  68. display_name = fields.Char(compute='_compute_display_name', store=True)
  69. name = fields.Char(string='前置单据编号', copy=False,
  70. readonly=True, required=True,
  71. help='该结算单编号,取自生成结算单的采购入库单和销售入库单')
  72. category_id = fields.Many2one(
  73. 'core.category',
  74. string='类别',
  75. domain="[('type', 'in', ('income','expense'))]",
  76. ondelete='restrict',
  77. default=_get_category_id,
  78. help='结算单类别:采购 或者 销售等')
  79. date = fields.Date(string='日期', required=True,
  80. default=lambda self: fields.Date.context_today(self),
  81. help='单据创建日期')
  82. amount = fields.Float(string='金额(含税)',
  83. digits='Amount',
  84. help='原始单据对应金额')
  85. reconciled = fields.Float(string='已核销金额', readonly=True,
  86. digits='Amount',
  87. help='原始单据已核销掉的金额')
  88. to_reconcile = fields.Float(string='未核销金额', readonly=True,
  89. digits='Amount',
  90. help='原始单据未核销掉的金额')
  91. tax_amount = fields.Float('税额',
  92. digits='Amount',
  93. help='对应税额')
  94. get_amount_date = fields.Date('最后收款日期', compute=_get_sell_amount_state,
  95. store=True, copy=False)
  96. auxiliary_id = fields.Many2one('auxiliary.financing', '辅助核算',
  97. help='辅助核算')
  98. pay_method = fields.Many2one('pay.method',
  99. string='付款方式',
  100. ondelete='restrict')
  101. date_due = fields.Date(string='到期日',
  102. help='结算单的到期日')
  103. currency_id = fields.Many2one('res.currency', '外币币别',
  104. help='原始单据对应的外币币别')
  105. bill_number = fields.Char('纸质发票号',
  106. help='纸质发票号')
  107. invoice_date = fields.Date('开票日期')
  108. is_init = fields.Boolean('是否初始化单')
  109. company_id = fields.Many2one(
  110. 'res.company',
  111. string='公司',
  112. change_default=True,
  113. default=lambda self: self.env.company)
  114. overdue_days = fields.Float('逾期天数', readonly=True,
  115. compute='compute_overdue',
  116. help='当前日期 - 到期日')
  117. overdue_amount = fields.Float('逾期金额', readonly=True,
  118. compute='compute_overdue',
  119. help='超过到期日后仍未核销的金额')
  120. note = fields.Char('备注',
  121. help='可填入到期日计算的依据')
  122. handwork = fields.Boolean('手工结算')
  123. def money_invoice_done(self):
  124. """
  125. 结算单审核方法
  126. """
  127. for inv in self:
  128. if inv.state == 'done':
  129. raise UserError('请不要重复确认')
  130. inv.reconciled = 0.0
  131. inv.to_reconcile = inv.amount
  132. inv.state = 'done'
  133. if inv.pay_method:
  134. inv.date_due = inv.pay_method.get_due_date(inv.invoice_date)
  135. else:
  136. inv.date_due = inv.partner_id.pay_method.get_due_date(
  137. inv.invoice_date)
  138. if inv.category_id.type == 'income':
  139. inv.partner_id.receivable += inv.amount
  140. if inv.category_id.type == 'expense':
  141. inv.partner_id.payable += inv.amount
  142. if inv.handwork:
  143. # 手工结算单开票日期
  144. inv.invoice_date = inv.date
  145. if inv.is_init:
  146. inv.bill_number = '期初余额'
  147. inv.invoice_date = inv.date
  148. return True
  149. def money_invoice_draft(self):
  150. """
  151. 结算单反审核方法
  152. :return:
  153. """
  154. for inv in self:
  155. if inv.state == 'draft':
  156. raise UserError('请不要重复撤销 %s' % self._description)
  157. if inv.reconciled != 0.0:
  158. raise UserError('已核销的结算单不允许撤销')
  159. # 查找发票相关的收款单
  160. source_line = self.env['source.order.line'].search(
  161. [('name', '=', inv.id)])
  162. for line in source_line:
  163. ref_name = False
  164. if line.money_id:
  165. ref_name = source_line.money_id.name
  166. if line.payable_reconcile_id:
  167. ref_name = line.payable_reconcile_id.name
  168. if line.receivable_reconcile_id:
  169. ref_name = line.receivable_reconcile_id.name
  170. if ref_name:
  171. raise UserError('发票已被单据 %s 引用,无法撤销' % ref_name)
  172. inv.reconciled = 0.0
  173. inv.to_reconcile = 0.0
  174. inv.state = 'draft'
  175. if inv.category_id.type == 'income':
  176. inv.partner_id.receivable -= inv.amount
  177. if inv.category_id.type == 'expense':
  178. inv.partner_id.payable -= inv.amount
  179. @api.model_create_multi
  180. def create(self, vals_list):
  181. """
  182. 创建结算单时,如果公司上的‘根据发票确认应收应付’字段没有勾上,则直接审核结算单,否则不审核。
  183. """
  184. new_ids = super(MoneyInvoice, self).create(vals_list)
  185. for new_id in new_ids:
  186. if not self.env.user.company_id.draft_invoice:
  187. new_id.money_invoice_done()
  188. return new_ids
  189. def write(self, values):
  190. """
  191. 当更新结算单到期日时,纸质发票号 相同的结算单到期日一起更新
  192. """
  193. if values.get('date_due') and self.bill_number \
  194. and not self.env.context.get('other_invoice_date_due'):
  195. invoices = self.search([('bill_number', '=', self.bill_number)])
  196. for inv in invoices:
  197. inv.with_context({'other_invoice_date_due': True}).write(
  198. {'date_due': values.get('date_due')})
  199. return super(MoneyInvoice, self).write(values)
  200. def unlink(self):
  201. """
  202. 只允许删除未确认的单据
  203. """
  204. for invoice in self:
  205. if invoice.name == '.' and invoice.reconciled == 0.0:
  206. self.money_invoice_draft()
  207. continue
  208. return super(MoneyInvoice, self).unlink()
  209. def find_source_order(self):
  210. '''
  211. 查看原始单据,有以下情况:销售发货单、销售退货单、采购退货单、采购入库单、
  212. 项目、委外加工单、核销单、采购订单、固定资产、固定资产变更以及期初应收应付。
  213. '''
  214. self.ensure_one()
  215. code = False
  216. res_models = [
  217. 'reconcile.order',
  218. ]
  219. views = [
  220. 'money.reconcile_order_form',
  221. ]
  222. # 判断当前数据库中否存在该 model
  223. if self.env.get('sell.delivery') is not None:
  224. res_models += ['sell.delivery']
  225. views += ['sell.sell_delivery_form']
  226. if self.env.get('outsource') is not None:
  227. res_models += ['outsource']
  228. views += ['warehouse.outsource_form']
  229. if self.env.get('buy.order') is not None:
  230. res_models += ['buy.order']
  231. views += ['buy.buy_order_form']
  232. if self.env.get('buy.receipt') is not None:
  233. res_models += ['buy.receipt']
  234. views += ['buy.buy_receipt_form']
  235. if self.env.get('project') is not None:
  236. res_models += ['project']
  237. views += ['task.project_form']
  238. if self.env.get('asset') is not None:
  239. res_models += ['asset']
  240. views += ['asset.asset_form']
  241. if self.env.get('cost.order') is not None:
  242. res_models += ['cost.order']
  243. views += ['account_cost.cost_order_form']
  244. if self.env.get('hr.expense') is not None:
  245. res_models += ['hr.expense']
  246. views += ['staff_expense.hr_expense_form']
  247. if '固定资产变更' in self.name:
  248. code = self.name.replace('固定资产变更', '')
  249. elif '固定资产' in self.name:
  250. code = self.name.replace('固定资产', '')
  251. domain = code and [('code', '=', code)] or [('name', '=', self.name)]
  252. for i in range(len(res_models)):
  253. # 若code存在说明 model为asset,view为固定资产form视图。
  254. res_model = code and 'asset' or res_models[i]
  255. view = code and self.env.ref(
  256. 'asset.asset_form') or self.env.ref(views[i])
  257. res = self.env[res_model].search(domain)
  258. if res: # 如果找到res_id,则退出for循环。
  259. break
  260. if not res:
  261. raise UserError('没有原始单据可供查看。')
  262. if res_model == 'sell.delivery' and res.is_return:
  263. view = self.env.ref('sell.sell_return_form')
  264. elif res_model == 'buy.receipt' and res.is_return:
  265. view = self.env.ref('buy.buy_return_form')
  266. return {
  267. 'view_mode': 'form',
  268. 'view_id': False,
  269. 'views': [(view.id, 'form')],
  270. 'res_model': res_model,
  271. 'type': 'ir.actions.act_window',
  272. 'res_id': res.id,
  273. }
上海开阖软件有限公司 沪ICP备12045867号-1