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.

449 lines
20KB

  1. # Copyright 2016 上海开阖软件有限公司 (http://www.osbzr.com)
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. from odoo.exceptions import UserError
  4. from odoo import fields, models, api
  5. from odoo.tools import float_compare
  6. class OtherMoneyOrder(models.Model):
  7. _name = 'other.money.order'
  8. _description = '其他收入/其他支出'
  9. _inherit = ['mail.thread', 'mail.activity.mixin']
  10. _order = 'id desc'
  11. TYPE_SELECTION = [
  12. ('other_pay', '其他支出'),
  13. ('other_get', '其他收入'),
  14. ]
  15. @api.model
  16. def create(self, values):
  17. # 创建单据时,更新订单类型的不同,生成不同的单据编号
  18. if self.env.context.get('type') == 'other_get':
  19. values.update(
  20. {'name': self.env['ir.sequence'].next_by_code(
  21. 'other.get.order')})
  22. if self.env.context.get('type') == 'other_pay' \
  23. or values.get('name', '/') == '/':
  24. values.update(
  25. {'name': self.env['ir.sequence'].next_by_code(
  26. 'other.pay.order')})
  27. return super(OtherMoneyOrder, self).create(values)
  28. @api.depends('line_ids.amount', 'line_ids.tax_amount')
  29. def _compute_total_amount(self):
  30. # 计算应付金额/应收金额
  31. for omo in self:
  32. omo.total_amount = sum((line.amount + line.tax_amount)
  33. for line in omo.line_ids)
  34. state = fields.Selection([
  35. ('draft', '草稿'),
  36. ('done', '已确认'),
  37. ('cancel', '已作废'),
  38. ], string='状态', readonly=True,
  39. default='draft', copy=False, index=True,
  40. tracking=True,
  41. help='其他收支单状态标识,新建时状态为草稿;确认后状态为已确认')
  42. partner_id = fields.Many2one('partner', string='往来单位',
  43. ondelete='restrict',
  44. readonly="state!='draft'",
  45. help='单据对应的业务伙伴,单据确认时会影响他的应收应付余额')
  46. date = fields.Date(string='单据日期',
  47. default=lambda self: fields.Date.context_today(self),
  48. readonly="state!='draft'",
  49. copy=False,
  50. help='单据创建日期')
  51. name = fields.Char(string='单据编号', copy=False, readonly=True, default='/',
  52. help='单据编号,创建时会根据类型自动生成')
  53. total_amount = fields.Float(string='金额', compute='_compute_total_amount',
  54. store=True, readonly=True,
  55. digits='Amount',
  56. help='本次其他收支的总金额')
  57. bank_id = fields.Many2one('bank.account', string='结算账户',
  58. required=True, ondelete='restrict',
  59. readonly="state!='draft'",
  60. help='本次其他收支的结算账户')
  61. line_ids = fields.One2many('other.money.order.line', 'other_money_id',
  62. string='收支单行',
  63. copy=True,
  64. readonly="state!='draft'",
  65. help='其他收支单明细行')
  66. type = fields.Selection(TYPE_SELECTION, string='类型',
  67. default=lambda self: self._context.get('type'),
  68. readonly="state!='draft'",
  69. help='类型:其他收入 或者 其他支出')
  70. note = fields.Text('备注',
  71. help='可以为该单据添加一些需要的标识信息')
  72. is_init = fields.Boolean('初始化应收应付', help='此单是否为初始化单')
  73. company_id = fields.Many2one(
  74. 'res.company',
  75. string='公司',
  76. change_default=True,
  77. default=lambda self: self.env.company)
  78. receiver = fields.Char('收款人',
  79. help='收款人')
  80. bank_name = fields.Char('开户行')
  81. bank_num = fields.Char('银行账号')
  82. voucher_id = fields.Many2one('voucher',
  83. '对应凭证',
  84. readonly=True,
  85. ondelete='restrict',
  86. copy=False,
  87. help='其他收支单确认时生成的对应凭证')
  88. currency_amount = fields.Float('外币金额',
  89. digits='Amount')
  90. details = fields.Html('明细', compute='_compute_details')
  91. @api.depends('line_ids')
  92. def _compute_details(self):
  93. for v in self:
  94. vl = {'col': [], 'val': []}
  95. vl['col'] = ['类别', '会计科目', '辅助核算', '金额']
  96. for li in v.line_ids:
  97. vl['val'].append([
  98. li.category_id.name,
  99. li.account_id.display_name,
  100. li.auxiliary_id.name or '',
  101. li.amount])
  102. v.details = v.company_id._get_html_table(vl)
  103. @api.onchange('date')
  104. def onchange_date(self):
  105. if self._context.get('type') == 'other_get':
  106. return {'domain': {'partner_id': [('c_category_id', '!=', False)]}}
  107. else:
  108. return {'domain': {'partner_id': [('s_category_id', '!=', False)]}}
  109. @api.onchange('partner_id')
  110. def onchange_partner_id(self):
  111. """
  112. 更改业务伙伴,自动填入收款人、开户行和银行帐号
  113. """
  114. if self.partner_id:
  115. self.receiver = self.partner_id.name
  116. self.bank_name = self.partner_id.bank_name
  117. self.bank_num = self.partner_id.bank_num
  118. def other_money_done(self):
  119. '''其他收支单的审核按钮'''
  120. self.ensure_one()
  121. if float_compare(self.total_amount, 0, 3) <= 0:
  122. raise UserError('金额应该大于0。\n金额:%s' % self.total_amount)
  123. if not self.bank_id.account_id:
  124. raise UserError('请配置%s的会计科目' % (self.bank_id.name))
  125. # 根据单据类型更新账户余额
  126. if self.type == 'other_pay':
  127. decimal_amount = self.env.ref('core.decimal_amount')
  128. if float_compare(
  129. self.bank_id.balance,
  130. self.total_amount,
  131. decimal_amount.digits) == -1:
  132. raise UserError('账户余额不足。\n账户余额:%s 本次支出金额:%s' %
  133. (self.bank_id.balance, self.total_amount))
  134. self.bank_id.balance -= self.total_amount
  135. else:
  136. self.bank_id.balance += self.total_amount
  137. # 创建凭证并审核非初始化凭证
  138. vouch_obj = self.create_voucher()
  139. return self.write({
  140. 'voucher_id': vouch_obj.id,
  141. 'state': 'done',
  142. })
  143. def other_money_draft(self):
  144. '''其他收支单的反审核按钮'''
  145. self.ensure_one()
  146. # 根据单据类型更新账户余额
  147. if self.type == 'other_pay':
  148. self.bank_id.balance += self.total_amount
  149. else:
  150. decimal_amount = self.env.ref('core.decimal_amount')
  151. if float_compare(
  152. self.bank_id.balance,
  153. self.total_amount,
  154. decimal_amount.digits) == -1:
  155. raise UserError('账户余额不足。\n账户余额:%s 本次支出金额:%s' %
  156. (self.bank_id.balance, self.total_amount))
  157. self.bank_id.balance -= self.total_amount
  158. voucher = self.voucher_id
  159. self.write({
  160. 'voucher_id': False,
  161. 'state': 'draft',
  162. })
  163. # 反审核凭证并删除
  164. if voucher.state == 'done':
  165. voucher.voucher_draft()
  166. # 始初化单反审核只删除明细行
  167. if self.is_init:
  168. vouch_obj = self.env['voucher'].search([('id', '=', voucher.id)])
  169. vouch_obj_lines = self.env['voucher.line'].search([
  170. ('voucher_id', '=', vouch_obj.id),
  171. ('account_id', '=', self.bank_id.account_id.id),
  172. ('init_obj', '=', 'other_money_order-%s' % (self.id))])
  173. for vouch_obj_line in vouch_obj_lines:
  174. vouch_obj_line.unlink()
  175. else:
  176. voucher.unlink()
  177. return True
  178. def create_voucher(self):
  179. """创建凭证并审核非初始化凭证"""
  180. init_obj = ''
  181. # 初始化单的话,先找是否有初始化凭证,没有则新建一个
  182. if self.is_init:
  183. vouch_obj = self.env['voucher'].search([('is_init', '=', True)])
  184. if not vouch_obj:
  185. vouch_obj = self.env['voucher'].create({
  186. 'date': self.date,
  187. 'is_init': True,
  188. 'ref': '%s,%s' % (self._name, self.id)})
  189. else:
  190. vouch_obj = self.env['voucher'].create(
  191. {'date': self.date, 'ref': '%s,%s' % (self._name, self.id)})
  192. if self.is_init:
  193. init_obj = 'other_money_order-%s' % (self.id)
  194. if self.type == 'other_get': # 其他收入单
  195. self.other_get_create_voucher_line(vouch_obj, init_obj)
  196. else: # 其他支出单
  197. self.other_pay_create_voucher_line(vouch_obj)
  198. # 如果非初始化单则审核
  199. if not self.is_init:
  200. vouch_obj.voucher_done()
  201. return vouch_obj
  202. def other_get_create_voucher_line(self, vouch_obj, init_obj):
  203. """
  204. 其他收入单生成凭证明细行
  205. :param vouch_obj: 凭证
  206. :return:
  207. """
  208. in_currency_id = (
  209. self.bank_id.currency_id.id
  210. or self.env.user.company_id.currency_id.id)
  211. company_currency_id = self.env.user.company_id.currency_id.id
  212. in_is_company_currency = in_currency_id == company_currency_id
  213. vals = {}
  214. for line in self.line_ids:
  215. if not line.category_id.account_id:
  216. raise UserError('请配置%s的会计科目' % (line.category_id.name))
  217. rate_silent = self.env['res.currency'].get_rate_silent(
  218. self.date, self.bank_id.currency_id.id)
  219. vals.update({'vouch_obj_id': vouch_obj.id,
  220. 'name': self.name, 'note': line.note or '',
  221. 'credit_auxiliary_id': line.auxiliary_id.id,
  222. 'amount': abs(line.amount + line.tax_amount),
  223. 'credit_account_id': line.category_id.account_id.id,
  224. 'debit_account_id': self.bank_id.account_id.id,
  225. 'partner_credit': self.partner_id.id,
  226. 'partner_debit': '',
  227. 'sell_tax_amount': line.tax_amount or 0,
  228. 'init_obj': init_obj,
  229. 'currency_id': self.bank_id.currency_id.id,
  230. 'currency_amount': (
  231. not in_is_company_currency
  232. and self.total_amount or 0),
  233. # 判断是否本币
  234. 'rate_silent': rate_silent,
  235. })
  236. # 贷方行
  237. if not init_obj:
  238. self.env['voucher.line'].create({
  239. 'name': "%s %s" % (vals.get('name'), vals.get('note')),
  240. 'partner_id': vals.get('partner_credit', ''),
  241. 'account_id': vals.get('credit_account_id'),
  242. 'credit': (
  243. in_is_company_currency
  244. and line.amount
  245. or line.amount * vals.get('rate_silent')),
  246. 'voucher_id': vals.get('vouch_obj_id'),
  247. 'auxiliary_id': vals.get('credit_auxiliary_id', False),
  248. 'currency_amount': (
  249. not in_is_company_currency
  250. and line.amount or 0),
  251. 'rate_silent': vals.get('rate_silent'),
  252. })
  253. # 销项税行
  254. if vals.get('sell_tax_amount'):
  255. if not self.env.user.company_id.output_tax_account:
  256. raise UserError(
  257. '您还没有配置公司的销项税科目。\n请通过"配置-->高级配置-->公司"菜单来设置销项税科目!')
  258. self.env['voucher.line'].create({
  259. 'name': "%s %s" % (vals.get('name'), vals.get('note')),
  260. 'account_id':
  261. self.env.user.company_id.output_tax_account.id,
  262. 'credit': in_is_company_currency
  263. and line.tax_amount
  264. or line.tax_amount * vals.get('rate_silent') or 0,
  265. 'voucher_id': vals.get('vouch_obj_id'),
  266. 'currency_amount':
  267. not in_is_company_currency
  268. and line.tax_amount or 0,
  269. 'rate_silent': vals.get('rate_silent'),
  270. })
  271. # 借方行
  272. self.env['voucher.line'].create({
  273. 'name': "%s" % (vals.get('name')),
  274. 'account_id': vals.get('debit_account_id'),
  275. # 借方和
  276. 'debit':
  277. in_is_company_currency
  278. and self.total_amount
  279. or self.total_amount * vals.get('rate_silent'),
  280. 'voucher_id': vals.get('vouch_obj_id'),
  281. 'partner_id': vals.get('partner_debit', ''),
  282. 'auxiliary_id': vals.get('debit_auxiliary_id', False),
  283. 'init_obj': vals.get('init_obj', False),
  284. 'currency_id': vals.get('currency_id', False),
  285. 'currency_amount': vals.get('currency_amount'),
  286. 'rate_silent': vals.get('rate_silent'),
  287. })
  288. def other_pay_create_voucher_line(self, vouch_obj):
  289. """
  290. 其他支出单生成凭证明细行
  291. :param vouch_obj: 凭证
  292. :return:
  293. """
  294. out_currency_id = \
  295. self.bank_id.currency_id.id \
  296. or self.env.user.company_id.currency_id.id
  297. company_currency_id = self.env.user.company_id.currency_id.id
  298. out_is_company_currency = out_currency_id == company_currency_id
  299. vals = {}
  300. for line in self.line_ids:
  301. if not line.category_id.account_id:
  302. raise UserError('请配置%s的会计科目' % (line.category_id.name))
  303. rate_silent = self.env['res.currency'].get_rate_silent(
  304. self.date, self.bank_id.currency_id.id)
  305. vals.update({'vouch_obj_id': vouch_obj.id, 'name': self.name,
  306. 'note': line.note or '',
  307. 'debit_auxiliary_id': line.auxiliary_id.id,
  308. 'amount': abs(line.amount + line.tax_amount),
  309. 'credit_account_id': self.bank_id.account_id.id,
  310. 'debit_account_id': line.category_id.account_id.id,
  311. 'partner_credit': '',
  312. 'partner_debit': self.partner_id.id,
  313. 'buy_tax_amount': line.tax_amount or 0,
  314. 'currency_id': self.bank_id.currency_id.id,
  315. 'currency_amount':
  316. not out_is_company_currency
  317. and self.total_amount or 0, # 判断是否本币
  318. 'rate_silent': rate_silent,
  319. })
  320. # 借方行
  321. self.env['voucher.line'].create({
  322. 'name': "%s %s " % (vals.get('name'), vals.get('note')),
  323. 'account_id': vals.get('debit_account_id'),
  324. 'debit':
  325. out_is_company_currency
  326. and line.amount
  327. or line.amount * vals.get('rate_silent'),
  328. 'voucher_id': vals.get('vouch_obj_id'),
  329. 'partner_id': vals.get('partner_debit', ''),
  330. 'auxiliary_id': vals.get('debit_auxiliary_id', False),
  331. 'init_obj': vals.get('init_obj', False),
  332. 'currency_amount':
  333. not out_is_company_currency
  334. and line.amount or 0,
  335. 'rate_silent': vals.get('rate_silent'),
  336. })
  337. # 进项税行
  338. if vals.get('buy_tax_amount'):
  339. if not self.env.user.company_id.import_tax_account:
  340. raise UserError('请通过"配置-->高级配置-->公司"菜单来设置进项税科目')
  341. self.env['voucher.line'].create({
  342. 'name': "%s %s" % (vals.get('name'), vals.get('note')),
  343. 'account_id':
  344. self.env.user.company_id.import_tax_account.id,
  345. 'debit':
  346. out_is_company_currency
  347. and line.tax_amount
  348. or line.tax_amount * vals.get('rate_silent') or 0,
  349. 'voucher_id': vals.get('vouch_obj_id'),
  350. 'currency_amount':
  351. not out_is_company_currency
  352. and line.tax_amount or 0,
  353. 'rate_silent': vals.get('rate_silent'),
  354. })
  355. # 贷方行
  356. self.env['voucher.line'].create({
  357. 'name': "%s" % (vals.get('name')),
  358. 'partner_id': vals.get('partner_credit', ''),
  359. 'account_id': vals.get('credit_account_id'),
  360. # 借方和
  361. 'credit':
  362. out_is_company_currency
  363. and self.total_amount
  364. or self.total_amount * vals.get('rate_silent'),
  365. 'voucher_id': vals.get('vouch_obj_id'),
  366. 'auxiliary_id': vals.get('credit_auxiliary_id', False),
  367. 'init_obj': vals.get('init_obj', False),
  368. 'currency_id': vals.get('currency_id', False),
  369. 'currency_amount': vals.get('currency_amount'),
  370. 'rate_silent': vals.get('rate_silent'),
  371. })
  372. class OtherMoneyOrderLine(models.Model):
  373. _name = 'other.money.order.line'
  374. _description = '其他收支单明细'
  375. @api.onchange('service')
  376. def onchange_service(self):
  377. # 当选择了收支项后,则自动填充上类别和金额
  378. if self.env.context.get('order_type') == 'other_get':
  379. self.category_id = self.service.get_categ_id.id
  380. elif self.env.context.get('order_type') == 'other_pay':
  381. self.category_id = self.service.pay_categ_id.id
  382. self.amount = self.service.price
  383. self.tax_rate = self.tax_rate
  384. @api.onchange('amount', 'tax_rate')
  385. def onchange_tax_amount(self):
  386. '''当订单行的金额、税率改变时,改变税额'''
  387. self.tax_amount = self.amount * self.tax_rate * 0.01
  388. other_money_id = fields.Many2one('other.money.order',
  389. '其他收支', ondelete='cascade',
  390. help='其他收支单行对应的其他收支单')
  391. service = fields.Many2one('service', '收支项', ondelete='restrict',
  392. help='其他收支单行上对应的收支项')
  393. category_id = fields.Many2one('core.category',
  394. '类别', ondelete='restrict',
  395. help='类型:运费、咨询费等')
  396. account_id = fields.Many2one('finance.account',
  397. '科目', related='category_id.account_id')
  398. auxiliary_id = fields.Many2one('auxiliary.financing', '辅助核算',
  399. help='其他收支单行上的辅助核算')
  400. amount = fields.Float('金额',
  401. digits='Amount',
  402. help='其他收支单行上的金额')
  403. tax_rate = fields.Float(
  404. '税率(%)',
  405. default=lambda self: self.env.user.company_id.import_tax_rate,
  406. help='其他收支单行上的税率')
  407. tax_amount = fields.Float('税额',
  408. digits='Amount',
  409. help='其他收支单行上的税额')
  410. note = fields.Char('备注',
  411. help='可以为该单据添加一些需要的标识信息')
  412. company_id = fields.Many2one(
  413. 'res.company',
  414. string='公司',
  415. change_default=True,
  416. default=lambda self: self.env.company)
上海开阖软件有限公司 沪ICP备12045867号-1