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

809 行
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