GoodERP
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

799 行
36KB

  1. from datetime import datetime
  2. from odoo import api, fields, models
  3. from odoo.exceptions import UserError, ValidationError
  4. # 字段只读状态
  5. READONLY_STATES = {
  6. 'done': [('readonly', True)], # 已审核
  7. 'clean': [('readonly', True)], # 已清理
  8. }
  9. class AssetCategory(models.Model):
  10. '''固定资产分类'''
  11. _name = 'asset.category'
  12. _inherit = ['mail.thread', 'mail.activity.mixin']
  13. _description = '固定资产分类'
  14. # 字段,命名问题很严重
  15. name = fields.Char('名称', required=True)
  16. # 一些带到固定资产上的默认值
  17. account_accumulated_depreciation = fields.Many2one(
  18. 'finance.account', '累计折旧科目', required=True)
  19. account_asset = fields.Many2one(
  20. 'finance.account', '固定资产科目', required=True)
  21. account_depreciation = fields.Many2one(
  22. 'finance.account', '折旧费用科目', required=True)
  23. depreciation_number = fields.Float('折旧期间数', required=True)
  24. depreciation_value = fields.Float('最终残值率%', required=True)
  25. clean_income = fields.Many2one(
  26. 'finance.account', '固定资产清理收入科目', required=True)
  27. clean_costs = fields.Many2one(
  28. 'finance.account', '固定资产清理成本科目', required=True)
  29. # 用于软删除
  30. active = fields.Boolean('生效', default=True)
  31. # 未来支持多公司
  32. company_id = fields.Many2one(
  33. 'res.company',
  34. string='公司',
  35. change_default=True,
  36. default=lambda self: self.env.company)
  37. class Asset(models.Model):
  38. _name = 'asset'
  39. _inherit = ['mail.thread', 'mail.activity.mixin']
  40. _description = '固定资产'
  41. _order = "code" # 按资产编码排序
  42. @api.depends('date')
  43. def _compute_period_id(self):
  44. ''' 根据入账日期获取期间,用于生成凭证 '''
  45. for s in self:
  46. s.period_id = self.env['finance.period'].get_period(s.date)
  47. @api.depends('cost', 'tax')
  48. def _get_amount(self):
  49. ''' 价税合计 '''
  50. for s in self:
  51. s.amount = s.cost + s.tax
  52. @api.depends('cost', 'depreciation_previous')
  53. def _get_surplus_value(self):
  54. ''' 计算固定资产原值和残值 '''
  55. for s in self:
  56. # 原值 = 购置成本 - (ERP系统上线前)已提折旧
  57. s.surplus_value = s.cost - s.depreciation_previous
  58. # 残值按固定资产分类上的残值比率计算
  59. s.depreciation_value = s.category_id.depreciation_value * s.cost / 100
  60. @api.depends('surplus_value', 'depreciation_value', 'depreciation_number','no_depreciation')
  61. def _get_cost_depreciation(self):
  62. ''' 计算每月折旧 '''
  63. for s in self:
  64. if s.no_depreciation == True: # 不提折旧不要算
  65. s.cost_depreciation = 0
  66. else: # 原值减残值减已折旧额,再除以剩余折旧期数
  67. dep_his_count = 0 # 已提期数
  68. dep_his_amount = 0 # 已提折旧
  69. for l in s.line_ids:
  70. dep_his_amount += l.cost_depreciation
  71. dep_his_count += 1
  72. if dep_his_count == s.depreciation_number:
  73. s.cost_depreciation = 0 # 已提完
  74. else:
  75. s.cost_depreciation = (s.surplus_value - s.depreciation_value
  76. - dep_his_amount) \
  77. / (s.depreciation_number - dep_his_count)
  78. @api.depends('surplus_value', 'line_ids','state')
  79. def _get_net_value(self):
  80. ''' 计算固定资产净值 '''
  81. for s in self:
  82. if s.state == 'clean': # 已清理的固定资产净值为0
  83. s.net_value = 0
  84. else: # 原值 - 折旧
  85. s.net_value = s.surplus_value - sum(
  86. [l.cost_depreciation for l in s.line_ids] )
  87. # 字段
  88. code = fields.Char('编码', required="1", states=READONLY_STATES)
  89. name = fields.Char('名称', required=True, states=READONLY_STATES)
  90. category_id = fields.Many2one(
  91. 'asset.category', '固定资产分类', ondelete='restrict', required=True, states=READONLY_STATES)
  92. cost = fields.Float('金额', digits='总数', required=True, states=READONLY_STATES)
  93. surplus_value = fields.Float('原值', digits='总数', store=True, compute='_get_surplus_value')
  94. net_value = fields.Float('净值',digits='总数', store=True, compute='_get_net_value')
  95. no_depreciation = fields.Boolean('不折旧')
  96. depreciation_number = fields.Integer(
  97. '折旧期间数', required=True, states=READONLY_STATES)
  98. depreciation_value = fields.Float('最终残值', digits='总数', required=True, states=READONLY_STATES)
  99. cost_depreciation = fields.Float('每月折旧额', digits='总数',
  100. store=True, compute='_get_cost_depreciation')
  101. state = fields.Selection([('draft', '草稿'),
  102. ('done', '已审核'),
  103. ('clean', '已清理')], '状态', default='draft',
  104. index=True,)
  105. period_id = fields.Many2one(
  106. 'finance.period',
  107. '会计期间',
  108. compute='_compute_period_id', ondelete='restrict', store=True)
  109. date = fields.Date('记帐日期', required=True, states=READONLY_STATES)
  110. tax = fields.Float('税额', digits='总数', required=True, states=READONLY_STATES)
  111. amount = fields.Float('价税合计', digits='总数', store=True, compute='_get_amount')
  112. partner_id = fields.Many2one('partner', '供应商', ondelete='restrict', states=READONLY_STATES,
  113. domain="[('s_category_id', '!=', False)]",
  114. help='用于记录采购固定资产时的应付账款,据此生成结算单')
  115. bank_account = fields.Many2one('bank.account', '账号', ondelete='restrict', states=READONLY_STATES,
  116. help='用于记录现金采购固定资产时的付款,据此生成其他支出单')
  117. is_init = fields.Boolean('初始化资产', states=READONLY_STATES,
  118. help='此固定资产在ERP系统启用前就已经有折旧了')
  119. depreciation_previous = fields.Float('以前折旧', digits='总数', required=True, states=READONLY_STATES)
  120. account_credit = fields.Many2one(
  121. 'finance.account', '资产贷方科目', required=True, states=READONLY_STATES,
  122. help='固定资产入账时:\n 如赊购,此处为应付科目;\n 如现购,此处为资金科目;\n 如自建,此处为在建工程')
  123. account_asset = fields.Many2one(
  124. 'finance.account', '固定资产科目', required=True, states=READONLY_STATES)
  125. account_depreciation = fields.Many2one(
  126. 'finance.account', '折旧费用科目', required=True, states=READONLY_STATES)
  127. account_accumulated_depreciation = fields.Many2one(
  128. 'finance.account', '累计折旧科目', required=True, states=READONLY_STATES)
  129. line_ids = fields.One2many('asset.line', 'order_id', '折旧明细行',
  130. states=READONLY_STATES, copy=False)
  131. chang_ids = fields.One2many('chang.line', 'order_id', '变更明细行',
  132. states=READONLY_STATES, copy=False)
  133. auxiliary_id = fields.Many2one(
  134. 'auxiliary.financing', '辅助核算', help='辅助核算是对账务处理的一种补充,即实现更广泛的账务处理,\
  135. 以适应企业管理和决策的需要.辅助核算一般通过核算项目来实现', ondelete='restrict')
  136. # 界面上不可见的字段
  137. voucher_id = fields.Many2one(
  138. 'voucher', '对应凭证', readonly=True, ondelete='restrict', copy=False)
  139. money_invoice = fields.Many2one(
  140. 'money.invoice', '对应结算单', readonly=True, ondelete='restrict', copy=False)
  141. other_money_order = fields.Many2one(
  142. 'other.money.order', '对应其他应付款单', readonly=True, ondelete='restrict', copy=False)
  143. # 未来支持多公司
  144. company_id = fields.Many2one(
  145. 'res.company',
  146. string='公司',
  147. change_default=True,
  148. default=lambda self: self.env.company)
  149. _sql_constraints = [
  150. ('code_uniq', 'unique(code)', '固定资产编码必须唯一')
  151. ]
  152. @api.onchange('category_id')
  153. def onchange_category_id(self):
  154. '''当固定资产分类发生变化时,折旧期间数,固定资产科目,累计折旧科目,最终残值同时变化'''
  155. if self.category_id:
  156. self.depreciation_number = self.category_id.depreciation_number
  157. self.account_asset = self.category_id.account_asset
  158. self.account_accumulated_depreciation = self.category_id.account_accumulated_depreciation
  159. self.account_depreciation = self.category_id.account_depreciation
  160. self.depreciation_value = self.category_id.depreciation_value * self.cost / 100
  161. @api.onchange('cost')
  162. def onchange_cost(self):
  163. '''当固定资产金额发生变化时,最终残值变化'''
  164. if self.cost:
  165. self.depreciation_value = self.category_id.depreciation_value * self.cost / 100
  166. @api.onchange('partner_id')
  167. def onchange_partner_id(self):
  168. '''当合作伙伴发生变化时,固定资产贷方科目变化'''
  169. if self.partner_id:
  170. self.account_credit = self.partner_id.s_category_id.account_id
  171. @api.onchange('bank_account')
  172. def onchange_bank_account(self):
  173. '''当结算帐户发生变化时,固定资产贷方科目变化'''
  174. if self.bank_account:
  175. self.account_credit = self.bank_account.account_id
  176. def _wrong_asset_done(self):
  177. ''' 固定资产确认入账前的验证 '''
  178. if self.state == 'done':
  179. raise UserError('请不要重复确认!')
  180. if self.period_id.is_closed:
  181. raise UserError('该会计期间(%s)已结账!不能确认' % self.period_id.name)
  182. if self.cost <= 0:
  183. raise UserError('金额必须大于0!\n金额:%s' % self.cost)
  184. if self.tax < 0:
  185. raise UserError('税额必须大于0!\n税额:%s' % self.tax)
  186. if self.depreciation_previous < 0:
  187. raise UserError('以前折旧必须大于0!\n折旧金额:%s' %
  188. self.depreciation_previous)
  189. def _partner_generate_invoice(self):
  190. ''' 赊购的方式,选择往来单位时,生成结算单 '''
  191. categ = self.env.ref('asset.asset_expense') # 固定资产采购
  192. # 创建结算单
  193. money_invoice = self.env['money.invoice'].create({
  194. 'name': '固定资产' + self.code,
  195. 'partner_id': self.partner_id.id,
  196. 'category_id': categ and categ.id,
  197. 'date': self.date,
  198. 'invoice_date': self.date,
  199. 'amount': self.amount,
  200. 'reconciled': 0,
  201. 'to_reconcile': self.amount,
  202. 'date_due': fields.Date.context_today(self),
  203. 'state': 'draft',
  204. 'tax_amount': self.tax
  205. })
  206. self.write({'money_invoice': money_invoice.id})
  207. ''' 因分类上只能设置一个固定资产科目,这里要用当前固定资产的对应科目替换凭证 '''
  208. # 如未自动确认,则确认一下结算单
  209. if money_invoice.state != 'done':
  210. money_invoice.money_invoice_done()
  211. # 找到结算单对应的凭证行并修改科目
  212. chang_account = self.env['voucher.line'].search(
  213. [('voucher_id', '=', money_invoice.voucher_id.id),
  214. ('account_id', '=', categ.account_id.id)])
  215. chang_account.write({'account_id': self.account_asset.id})
  216. return money_invoice
  217. def _bank_account_generate_other_pay(self):
  218. ''' 现金和银行支付的方式,选择账号,生成其他支出单 '''
  219. category = self.env.ref('asset.asset') # 借:固定资产
  220. other_money_order = self.with_context(type='other_pay').env['other.money.order'].create({
  221. 'state': 'draft',
  222. 'partner_id': self.partner_id.id,
  223. 'date': self.date,
  224. 'bank_id': self.bank_account.id,
  225. })
  226. self.write({'other_money_order': other_money_order.id})
  227. self.env['other.money.order.line'].create({
  228. 'other_money_id': other_money_order.id,
  229. 'amount': self.cost,
  230. 'tax_rate': self.cost and self.tax / self.cost * 100 or 0,
  231. 'tax_amount': self.tax,
  232. 'category_id': category and category.id
  233. })
  234. return other_money_order
  235. def _construction_generate_voucher(self):
  236. ''' 贷方科目选择在建工程,直接生成凭证 '''
  237. vals = {}
  238. vouch_obj = self.env['voucher'].create({'date': self.date, 'ref': '%s,%s' % (self._name, self.id)})
  239. self.write({'voucher_id': vouch_obj.id})
  240. vals.update({'vouch_obj_id': vouch_obj.id, 'string': self.name, 'name': '固定资产',
  241. 'amount': self.amount, 'credit_account_id': self.account_credit.id,
  242. 'debit_account_id': self.account_asset.id,
  243. 'buy_tax_amount': self.tax or 0
  244. })
  245. self.env['money.invoice'].create_voucher_line(vals)
  246. vouch_obj.voucher_done()
  247. return vouch_obj
  248. def asset_done(self):
  249. ''' 确认固定资产 '''
  250. for s in self:
  251. s._wrong_asset_done()
  252. # 非初始化固定资产生成入账凭证
  253. if not s.is_init:
  254. if s.partner_id and s.partner_id.s_category_id.account_id == s.account_credit:
  255. # 赊购
  256. s._partner_generate_invoice()
  257. elif s.bank_account and s.account_credit == s.bank_account.account_id:
  258. # 现金购入
  259. s._bank_account_generate_other_pay()
  260. else:
  261. # 在建工程转入
  262. s._construction_generate_voucher()
  263. # 初始化的固定资产需要在初始化会计凭证上点击【导入固定资产】按钮
  264. s.state = 'done'
  265. return True
  266. def asset_draft(self):
  267. ''' 撤销确认固定资产 '''
  268. for s in self:
  269. if s.state == 'draft':
  270. raise UserError('请不要重复撤销 %s' % s._description)
  271. if s.line_ids:
  272. raise UserError('已折旧不能撤销确认!')
  273. if s.chang_ids:
  274. raise UserError('已变更不能撤销确认!')
  275. if s.period_id.is_closed:
  276. raise UserError('该会计期间(%s)已结账!不能撤销确认' % s.period_id.name)
  277. if s.money_invoice.reconciled != 0:
  278. raise UserError('固定资产已有核销,请不要撤销')
  279. '''删掉凭证'''
  280. if s.voucher_id:
  281. Voucher, s.voucher_id = s.voucher_id, False
  282. if Voucher.state == 'done':
  283. Voucher.voucher_draft()
  284. Voucher.unlink()
  285. '''删掉其他应付款单'''
  286. if s.other_money_order:
  287. other_money_order, s.other_money_order = s.other_money_order, False
  288. if other_money_order.state == 'done':
  289. other_money_order.other_money_draft()
  290. other_money_order.unlink()
  291. '''删掉结算单'''
  292. if s.money_invoice:
  293. money_invoice, s.money_invoice = s.money_invoice, False
  294. if money_invoice.state == 'done':
  295. money_invoice.money_invoice_draft()
  296. money_invoice.unlink()
  297. s.state = 'draft'
  298. return True
  299. class CreateCleanWizard(models.TransientModel):
  300. '''固定资产清理'''
  301. _name = 'create.clean.wizard'
  302. _description = '固定资产清理向导'
  303. @api.depends('date')
  304. def _compute_period_id(self):
  305. ''' 根据清理日期取得期间 '''
  306. for s in self:
  307. s.period_id = s.env['finance.period'].get_period(s.date)
  308. #字段
  309. date = fields.Date('清理日期', required=True)
  310. period_id = fields.Many2one(
  311. 'finance.period',
  312. '会计期间',
  313. compute='_compute_period_id', ondelete='restrict', store=True)
  314. clean_cost = fields.Float('清理费用', required=True)
  315. residual_income = fields.Float('残值收入', required=True)
  316. sell_tax_amount = fields.Float('销项税额', required=True)
  317. bank_account = fields.Many2one('bank.account', '账号')
  318. company_id = fields.Many2one(
  319. 'res.company',
  320. string='公司',
  321. change_default=True,
  322. default=lambda self: self.env.company)
  323. def _generate_other_get(self):
  324. '''按发票收入生成收入单'''
  325. get_category = self.env.ref('asset.asset_clean_get')
  326. other_money_order = self.with_context(type='other_get').env['other.money.order'].create({
  327. 'state': 'draft',
  328. 'partner_id': None,
  329. 'date': self.date,
  330. 'bank_id': self.bank_account.id,
  331. })
  332. self.env['other.money.order.line'].create({
  333. 'other_money_id': other_money_order.id,
  334. 'amount': self.residual_income,
  335. 'tax_rate': self.residual_income and self.sell_tax_amount / self.residual_income * 100 or 0,
  336. 'tax_amount': self.sell_tax_amount,
  337. 'category_id': get_category and get_category.id
  338. })
  339. def _clean_cost_generate_other_pay(self, clean_cost):
  340. '''按费用生成支出单'''
  341. pay_category = self.env.ref('asset.asset_clean_pay')
  342. other_money_order = self.with_context(type='other_pay').env['other.money.order'].create({
  343. 'state': 'draft',
  344. 'partner_id': None,
  345. 'date': self.date,
  346. 'bank_id': self.bank_account.id,
  347. })
  348. self.env['other.money.order.line'].create({
  349. 'other_money_id': other_money_order.id,
  350. 'amount': clean_cost,
  351. 'category_id': pay_category and pay_category.id
  352. })
  353. def _generate_voucher(self, Asset):
  354. ''' 生成凭证,并确认 '''
  355. vouch_obj = self.env['voucher'].create({'date': self.date, 'ref': '%s,%s' % (Asset._name, Asset.id)})
  356. depreciation2 = sum(line.cost_depreciation for line in Asset.line_ids)
  357. depreciation = Asset.depreciation_previous + depreciation2
  358. income = Asset.cost - depreciation
  359. Asset.write({'voucher_id': vouch_obj.id})
  360. '''借方行'''
  361. if income:
  362. self.env['voucher.line'].create({'voucher_id': vouch_obj.id, 'name': '清理固定资产',
  363. 'debit': income, 'account_id': Asset.category_id.clean_costs.id,
  364. })
  365. if depreciation:
  366. self.env['voucher.line'].create({'voucher_id': vouch_obj.id, 'name': '清理固定资产',
  367. 'debit': depreciation, 'account_id': Asset.account_accumulated_depreciation.id,
  368. })
  369. '''贷方行'''
  370. self.env['voucher.line'].create({'voucher_id': vouch_obj.id, 'name': '清理固定资产',
  371. 'credit': Asset.cost, 'account_id': Asset.account_asset.id,
  372. })
  373. vouch_obj.voucher_done()
  374. def create_clean_account(self):
  375. ''' 清理固定资产 '''
  376. for s in self:
  377. if not s.env.context.get('active_id'):
  378. return
  379. Asset = s.env['asset'].browse(s.env.context.get('active_id'))
  380. Asset.no_depreciation = 1
  381. Asset.state = 'clean'
  382. # 按发票收入生成收入单
  383. s._generate_other_get()
  384. # 按费用生成支出单
  385. if s.clean_cost:
  386. s._clean_cost_generate_other_pay(s.clean_cost)
  387. # 生成凭证
  388. s._generate_voucher(Asset)
  389. class CreateChangWizard(models.TransientModel):
  390. '''固定资产变更'''
  391. _name = 'create.chang.wizard'
  392. _description = '固定资产变更向导'
  393. @api.depends('chang_date')
  394. def _compute_period_id(self):
  395. ''' 根据变更日期取会计期间 '''
  396. for s in self:
  397. s.period_id = s.env['finance.period'].get_period(s.chang_date)
  398. # 字段
  399. chang_date = fields.Date('变更日期', required=True)
  400. period_id = fields.Many2one(
  401. 'finance.period',
  402. '会计期间',
  403. compute='_compute_period_id', ondelete='restrict', store=True)
  404. chang_cost = fields.Float('增加金额', required=True,
  405. digits='总数')
  406. chang_depreciation_number = fields.Float('变更折旧期间', required=True)
  407. chang_tax = fields.Float(
  408. '增加税额', digits='总数', required=True)
  409. chang_partner_id = fields.Many2one(
  410. 'partner', '供应商', ondelete='restrict', required=True)
  411. change_reason = fields.Text('变更原因')
  412. company_id = fields.Many2one(
  413. 'res.company',
  414. string='公司',
  415. change_default=True,
  416. default=lambda self: self.env.company)
  417. def create_chang_account(self):
  418. ''' 创建变更对应的结算单,确认应付 '''
  419. ''' TODO 逻辑似乎不太对,原值和折旧期的变更都会引起每月折旧的金额变化,但是已经提过的折旧差异没有处理 '''
  420. for s in self:
  421. if not s.env.context.get('active_id'):
  422. return
  423. Asset = s.env['asset'].browse(s.env.context.get('active_id'))
  424. if s.chang_cost > 0:
  425. chang_before_cost = Asset.cost
  426. chang_before_depreciation_number = Asset.depreciation_number
  427. Asset.cost = s.chang_cost + Asset.cost # 历史成本
  428. Asset.surplus_value = Asset.cost - Asset.depreciation_previous # 入账价值
  429. Asset.tax = Asset.tax + s.chang_tax # 税
  430. categ = s.env.ref('money.core_category_purchase')
  431. money_invoice = s.env['money.invoice'].create({
  432. 'name': '固定资产变更' + Asset.code,
  433. 'partner_id': s.chang_partner_id.id,
  434. 'category_id': categ.id,
  435. 'date': s.chang_date,
  436. 'amount': s.chang_cost + s.chang_tax,
  437. 'reconciled': 0,
  438. 'to_reconcile': s.chang_cost + s.chang_tax,
  439. 'date_due': fields.Date.context_today(s),
  440. 'state': 'draft',
  441. 'tax_amount': s.chang_tax
  442. })
  443. # 如未自动确认,则确认一下结算单
  444. if money_invoice.state != 'done':
  445. money_invoice.money_invoice_done()
  446. #将分类上的资产科目替换为固定资产上的资产科目
  447. chang_account = s.env['voucher.line'].search(
  448. [('voucher_id', '=',money_invoice.voucher_id.id),
  449. ('account_id', '=', categ.account_id.id)])
  450. chang_account.write({'account_id': Asset.account_asset.id})
  451. # 记录变更历史 - 原值变更
  452. s.env['chang.line'].create({'date': s.chang_date, 'period_id': s.period_id.id,
  453. 'chang_before': chang_before_cost,
  454. 'change_reason': s.change_reason,
  455. 'chang_after': Asset.cost, 'chang_name': '原值变更',
  456. 'order_id': Asset.id, 'partner_id': s.chang_partner_id.id
  457. })
  458. Asset.depreciation_number = Asset.depreciation_number + \
  459. s.chang_depreciation_number # 折旧期数
  460. Asset.depreciation_value = Asset.depreciation_value + Asset.category_id.depreciation_value * \
  461. s.chang_cost / 100 # 残值
  462. if s.chang_depreciation_number:
  463. s.env['chang.line'].create({'date': s.chang_date, 'period_id': s.period_id.id,
  464. 'chang_before': chang_before_depreciation_number,
  465. 'change_reason': s.change_reason,
  466. 'chang_after': Asset.depreciation_number, 'chang_name': '折旧期间变更',
  467. 'order_id': Asset.id, 'partner_id': s.chang_partner_id.id
  468. })
  469. return True
  470. class AssetLine(models.Model):
  471. _name = 'asset.line'
  472. _description = '资产折旧明细'
  473. @api.depends('date')
  474. def _compute_period_id(self):
  475. ''' 根据记账日期取会计期间 '''
  476. for s in self:
  477. s.period_id = s.env['finance.period'].get_period(s.date)
  478. # 查看固定资产卡片
  479. def view_asset(self):
  480. view = self.env.ref('asset.asset_form_readonly')
  481. return {
  482. 'name': '固定资产卡片',
  483. 'view_mode': 'form',
  484. 'view_id': False,
  485. 'views': [(view.id, 'form')],
  486. 'res_model': 'asset',
  487. 'type': 'ir.actions.act_window',
  488. 'target':'new',
  489. 'res_id': self.order_id.id,
  490. }
  491. order_id = fields.Many2one('asset', '资产', index=True,
  492. required=True, ondelete='restrict')
  493. category_id = fields.Many2one(
  494. 'asset.category',
  495. string='固定资产分类',
  496. related='order_id.category_id',
  497. store=True )
  498. net_value = fields.Float('净值',digits='总数')
  499. cost_depreciation = fields.Float(
  500. '折旧额', required=True, digits='总数')
  501. no_depreciation = fields.Float('未提折旧额')
  502. code = fields.Char('编码')
  503. name = fields.Char('名称')
  504. date = fields.Date('记帐日期', required=True)
  505. period_id = fields.Many2one(
  506. 'finance.period',
  507. '会计期间',
  508. compute='_compute_period_id', ondelete='restrict', store=True)
  509. auxiliary_id = fields.Many2one(
  510. 'auxiliary.financing', '辅助核算',
  511. related="order_id.auxiliary_id", store=True)
  512. company_id = fields.Many2one(
  513. 'res.company',
  514. string='公司',
  515. change_default=True,
  516. default=lambda self: self.env.company)
  517. class CreateDepreciationWizard(models.TransientModel):
  518. """生成每月折旧的向导 根据输入的期间"""
  519. _name = "create.depreciation.wizard"
  520. _description = '资产折旧向导'
  521. @api.depends('date')
  522. def _compute_period_id(self):
  523. '''根据输入的日期取期间'''
  524. for s in self:
  525. s.period_id = s.env['finance.period'].get_period(s.date)
  526. @api.model
  527. def _get_last_date(self):
  528. ''' 取本月的最后一天作为默认折旧日 '''
  529. date_now_period_id = self.env['finance.period'].get_date_now_period_id()
  530. if not date_now_period_id:
  531. raise UserError('当前日期对应的会计期间不存在,请先创建!')
  532. (first,last) = self.env['finance.period'].get_period_month_date_range(date_now_period_id)
  533. return last
  534. date = fields.Date('记帐日期', required=True, default=_get_last_date)
  535. period_id = fields.Many2one(
  536. 'finance.period',
  537. '会计期间',
  538. compute='_compute_period_id', ondelete='restrict', store=True)
  539. company_id = fields.Many2one(
  540. 'res.company',
  541. string='公司',
  542. change_default=True,
  543. default=lambda self: self.env.company)
  544. def _get_voucher_line(self, Asset, cost_depreciation, vouch_obj):
  545. ''' 借:累计折旧 '''
  546. res = {}
  547. if Asset.account_depreciation.id not in res:
  548. res[Asset.account_depreciation.id] = {'debit': 0}
  549. val = res[Asset.account_depreciation.id]
  550. val.update({'debit': val.get('debit') + cost_depreciation,
  551. 'voucher_id': vouch_obj.id,
  552. 'account_id': Asset.account_depreciation.id,
  553. 'auxiliary_id': Asset.auxiliary_id.id,
  554. 'name': '固定资产折旧',
  555. })
  556. ''' 贷:费用科目 '''
  557. if Asset.account_accumulated_depreciation.id not in res:
  558. res[Asset.account_accumulated_depreciation.id] = {'credit': 0}
  559. val = res[Asset.account_accumulated_depreciation.id]
  560. val.update({'credit': val.get('credit') + cost_depreciation,
  561. 'voucher_id': vouch_obj.id,
  562. 'account_id': Asset.account_accumulated_depreciation.id,
  563. 'name': '固定资产折旧',
  564. })
  565. return res
  566. def _generate_asset_line(self, Asset, cost_depreciation, total):
  567. '''生成折旧明细行'''
  568. AssetLine = self.env['asset.line'].create({
  569. 'date': self.date,
  570. 'order_id': Asset.id,
  571. 'company_id': Asset.company_id.id,
  572. 'period_id': self.period_id.id,
  573. 'cost_depreciation': cost_depreciation,
  574. 'name': Asset.name,
  575. 'code': Asset.code,
  576. # 未提折旧:原值 - 已提折旧 - 本期折旧
  577. 'no_depreciation': Asset.surplus_value - total - cost_depreciation,
  578. # 净值:未提折旧 + 残值
  579. 'net_value' : Asset.surplus_value - total - cost_depreciation + Asset.depreciation_value ,
  580. })
  581. return AssetLine
  582. def create_depreciation(self):
  583. ''' 资产折旧,生成凭证和折旧明细'''
  584. for s in self:
  585. vouch_obj = s.env['voucher'].create({'date': s.date})
  586. res = []
  587. asset_line_id_list = []
  588. for Asset in s.env['asset'].search([('no_depreciation', '=', False), # 提折旧的
  589. ('state', '=', 'done'), # 已审核
  590. ('period_id', '!=', s.period_id.id)]): # 从入账下月开始
  591. # 本期间没有折旧过,本期间晚于固定资产入账期间
  592. if s.period_id not in [line.period_id for line in Asset.line_ids] and \
  593. s.env['finance.period'].period_compare(s.period_id, Asset.period_id) > 0:
  594. # 本月折旧
  595. cost_depreciation = Asset.cost_depreciation
  596. # 累计折旧
  597. total = sum(
  598. line.cost_depreciation for line in Asset.line_ids) + Asset.depreciation_value
  599. # 最后一次折旧
  600. if Asset.surplus_value <= (total + cost_depreciation):
  601. cost_depreciation = Asset.surplus_value - total
  602. Asset.no_depreciation = 1
  603. # 构造凭证明细行字典
  604. res.append(s._get_voucher_line(
  605. Asset, cost_depreciation, vouch_obj))
  606. # 生成折旧明细行
  607. asset_line_row = s._generate_asset_line(
  608. Asset, cost_depreciation, total)
  609. asset_line_id_list.append(asset_line_row.id)
  610. # 构造凭证明细行字典
  611. debit_line_dict, credit_line_dict = {}, {}
  612. for i in range(len(res)):
  613. for account_id, val in res[i].items():
  614. # 生成借方凭证明细
  615. if 'debit' in list(val.keys()):
  616. auxiliary_id = val.get('auxiliary_id')
  617. if (account_id, auxiliary_id) not in debit_line_dict:
  618. debit_line_dict[(account_id, auxiliary_id)] = val
  619. else:
  620. debit_line_dict[(account_id, auxiliary_id)]['debit'] += val['debit']
  621. # 生成贷方凭证明细
  622. if 'credit' in list(val.keys()):
  623. if account_id not in credit_line_dict:
  624. credit_line_dict[account_id] = val
  625. else:
  626. credit_line_dict[account_id]['credit'] += val['credit']
  627. line_dict = dict(list(debit_line_dict.items()) + list(credit_line_dict.items()))
  628. for account_id, val in line_dict.items():
  629. s.env['voucher.line'].create(val) # 创建凭证行
  630. # 没有凭证行则报错
  631. if not vouch_obj.line_ids:
  632. raise UserError('本期没有需要折旧的固定资产。')
  633. #vouch_obj.voucher_done()
  634. # 界面转到本月折旧明细
  635. view = s.env.ref('asset.asset_line_tree')
  636. return {
  637. 'view_mode': 'list',
  638. 'name': '资产折旧明细行',
  639. 'views': [(view.id, 'list')],
  640. 'res_model': 'asset.line',
  641. 'type': 'ir.actions.act_window',
  642. 'target': 'main',
  643. 'domain': [('id', 'in', asset_line_id_list)]
  644. }
  645. class ChangLine(models.Model):
  646. _name = 'chang.line'
  647. _description = '资产变更明细'
  648. @api.depends('date')
  649. def _compute_period_id(s):
  650. ''' 根据变更日期取会计期间 '''
  651. for s in s:
  652. s.period_id = s.env['finance.period'].get_period(s.date)
  653. # 字段
  654. order_id = fields.Many2one('asset', '订单编号', index=True,
  655. required=True, ondelete='cascade')
  656. chang_name = fields.Char('变更内容', required=True)
  657. date = fields.Date('记帐日期', required=True)
  658. period_id = fields.Many2one(
  659. 'finance.period',
  660. '会计期间',
  661. compute='_compute_period_id', ondelete='restrict', store=True)
  662. chang_before = fields.Float('变更前')
  663. chang_after = fields.Float('变更后')
  664. chang_money_invoice = fields.Many2one(
  665. 'money.invoice', '对应结算单', readonly=True, ondelete='restrict')
  666. partner_id = fields.Many2one('partner', '供应商')
  667. change_reason = fields.Text('变更原因')
  668. company_id = fields.Many2one(
  669. 'res.company',
  670. string='公司',
  671. change_default=True,
  672. default=lambda self: self.env.company)
  673. class Voucher(models.Model):
  674. ''' 在凭证上增加 引入固定资产 按钮逻辑 '''
  675. _inherit = 'voucher'
  676. def init_asset(self):
  677. '''删除以前引入的固定资产内容'''
  678. for s in self:
  679. for line in s.line_ids:
  680. if line.init_obj == 'asset':
  681. line.unlink()
  682. '''引入固定资产初始化单据'''
  683. res = {}
  684. if s.env['asset'].search([('is_init', '=', True),
  685. ('state', '=', 'draft')]):
  686. raise UserError('有未确认的初始化固定资产')
  687. for Asset in s.env['asset'].search([('is_init', '=', True),
  688. ('state', '=', 'done')]):
  689. cost = Asset.cost
  690. depreciation_previous = Asset.depreciation_previous
  691. '''固定资产'''
  692. if Asset.account_asset.id not in res:
  693. res[Asset.account_asset.id] = {'credit': 0, 'debit': 0}
  694. val = res[Asset.account_asset.id]
  695. val.update({'debit': val.get('debit') + cost,
  696. 'account_id': Asset.account_asset.id,
  697. 'voucher_id': s.id,
  698. 'init_obj': 'asset',
  699. 'name': '固定资产 期初'
  700. })
  701. '''累计折旧'''
  702. if Asset.account_accumulated_depreciation.id not in res:
  703. res[Asset.account_accumulated_depreciation.id] = {
  704. 'credit': 0, 'debit': 0}
  705. val = res[Asset.account_accumulated_depreciation.id]
  706. val.update({'credit': val.get('credit') + depreciation_previous,
  707. 'account_id': Asset.account_accumulated_depreciation.id,
  708. 'voucher_id': s.id,
  709. 'init_obj': 'asset',
  710. 'name': '固定资产 期初'
  711. })
  712. for account_id, val in res.items():
  713. s.env['voucher.line'].create(dict(val, account_id=account_id),
  714. )
上海开阖软件有限公司 沪ICP备12045867号-1