GoodERP
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

309 rindas
12KB

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