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.

809 lines
36KB

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