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.

177 lines
6.8KB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 上海开阖软件有限公司 (http://www.osbzr.com)
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  4. from odoo import api, fields, models, _
  5. from odoo.exceptions import UserError
  6. AVAILABLE_PRIORITIES = [
  7. ('0', 'E'),
  8. ('1', 'D'),
  9. ('2', 'C'),
  10. ('3', 'B'),
  11. ('4', 'A'),
  12. ]
  13. class Partner(models.Model):
  14. '''
  15. odoo 中 res.partner 被过度使用,导致很多针对客户的控制逻辑很难实现。
  16. GoodERP采用了全新的业务伙伴对象。
  17. c_category_id 非空 业务伙伴是客户。
  18. s_category_id 非空 业务伙伴是供应商。
  19. '''
  20. _name = 'partner'
  21. _description = '业务伙伴'
  22. _inherit = ['mail.thread', 'mail.activity.mixin']
  23. code = fields.Char('编号')
  24. name = fields.Char('名称', required=True,)
  25. display_name = fields.Char(
  26. '显示名称',
  27. compute='_compute_partner_display_name',
  28. store=True,
  29. readonly=True,
  30. )
  31. @api.depends('code', 'name')
  32. def _compute_partner_display_name(self):
  33. for partner in self:
  34. if partner.code:
  35. partner.display_name = f'[{partner.code}]{partner.name}'
  36. else:
  37. partner.display_name = partner.name
  38. main_mobile = fields.Char('主要联系方式', required=False,)
  39. main_address = fields.Char('注册地址 电话')
  40. priority = fields.Selection(AVAILABLE_PRIORITIES,
  41. '客户重要性', default='0')
  42. supp_priority = fields.Selection(AVAILABLE_PRIORITIES,
  43. '供应商重要性', default='0')
  44. c_category_id = fields.Many2one('core.category', '客户类别',
  45. ondelete='restrict',
  46. domain=[('type', '=', 'customer')])
  47. s_category_id = fields.Many2one('core.category', '供应商类别',
  48. ondelete='restrict',
  49. domain=[('type', '=', 'supplier')])
  50. receivable = fields.Float('应收余额', readonly=True,
  51. copy=False,
  52. digits='Amount')
  53. payable = fields.Float('应付余额', readonly=True,
  54. copy=False,
  55. digits='Amount')
  56. tax_num = fields.Char('税务登记号')
  57. tax_rate = fields.Float('税率(%)',
  58. help='业务伙伴税率')
  59. bank_name = fields.Char('开户行')
  60. bic = fields.Char('行号')
  61. bank_num = fields.Char('银行账号')
  62. credit_limit = fields.Float(
  63. string='信用额度',
  64. tracking=True,
  65. help='客户购买商品时,一般本次发货金额+客户应收余额要小于客户信用额度')
  66. recon_day = fields.Integer("对账日")
  67. active = fields.Boolean('启用', default=True)
  68. company_id = fields.Many2one(
  69. 'res.company',
  70. string='公司',
  71. change_default=True,
  72. default=lambda self: self.env.company)
  73. tag_ids = fields.Many2many(
  74. 'core.value',
  75. ondelete='restrict',
  76. string='标签',
  77. domain=[('type', '=', 'partner_tag')])
  78. channel_id = fields.Many2one(
  79. 'core.value',
  80. string='渠道',
  81. ondelete='restrict',
  82. domain=[('type', '=', 'channel')],
  83. context={'type': 'channel'})
  84. source = fields.Char('来源')
  85. note = fields.Text('备注')
  86. main_contact = fields.Char('主联系人')
  87. responsible_id = fields.Many2one(
  88. 'res.users',
  89. tracking=True,
  90. ondelete='cascade',
  91. string='负责人员')
  92. share_id = fields.Many2one('res.users',
  93. '共享人员')
  94. pay_method = fields.Many2one('pay.method',
  95. string='付款方式',
  96. tracking=True,
  97. ondelete='restrict')
  98. date_qualify = fields.Date('资质到期日期')
  99. days_qualify = fields.Float('资质到期天数',
  100. compute='compute_days_qualify',
  101. store=True,
  102. help='当天到资质到期日期的天数差',
  103. )
  104. _sql_constraints = [
  105. ('name_uniq', 'unique(name)', '业务伙伴不能重名')
  106. ]
  107. @api.constrains('name', 'c_category_id', 's_category_id')
  108. def _check_category_exists(self):
  109. # 客户 或 供应商 类别有一个必输
  110. if self.name and not self.s_category_id and not self.c_category_id:
  111. raise UserError(_('请选择类别'))
  112. @api.model
  113. def name_search(self, name='', args=None, operator='ilike', limit=100):
  114. """
  115. 在many2one字段中支持按编号搜索
  116. """
  117. args = args or []
  118. if name:
  119. res_id = self.search([('code', '=', name)])
  120. if res_id:
  121. return [(record.id, record.display_name)
  122. for record in res_id]
  123. args.append(('code', 'ilike', name))
  124. partners = self.search(args)
  125. if partners:
  126. return [(record.id, record.display_name)
  127. for record in partners]
  128. else:
  129. args.remove(('code', 'ilike', name))
  130. result = super(Partner, self).name_search(name=name,
  131. args=args,
  132. operator=operator,
  133. limit=limit)
  134. return result
  135. def write(self, vals):
  136. # 业务伙伴应收/应付余额不为0时,不允许取消对应的客户/供应商身份
  137. if 'c_category_id' in list(vals.keys()) and \
  138. self.c_category_id and not vals.get('c_category_id') \
  139. and self.receivable != 0:
  140. raise UserError(_('该客户应收余额不为0,不能取消客户类型'))
  141. if 's_category_id' in list(vals.keys()) and \
  142. self.s_category_id and not vals.get('s_category_id') \
  143. and self.payable != 0:
  144. raise UserError(_('该供应商应付余额不为0,不能取消供应商类型'))
  145. return super(Partner, self).write(vals)
  146. def copy(self, default=None):
  147. ''' 避免复制时名称重复 '''
  148. if default is None:
  149. default = {}
  150. if 'name' not in default:
  151. default.update(name=_('%s (copy)') % (self.name))
  152. return super().copy(default=default)
  153. @api.depends('date_qualify')
  154. def compute_days_qualify(self):
  155. for partner in self:
  156. """计算当天距离资质到期日期的天数"""
  157. day = 0
  158. if partner.date_qualify:
  159. day = (
  160. partner.date_qualify - fields.Date.context_today(self)
  161. ).days
  162. partner.days_qualify = day
上海开阖软件有限公司 沪ICP备12045867号-1