GoodERP
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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