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

403 行
18KB

  1. # Copyright 2016 上海开阖软件有限公司 (http://www.osbzr.com)
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  3. from odoo import fields, models, api, tools, modules, _
  4. from datetime import datetime
  5. from odoo.exceptions import UserError
  6. import re
  7. from random import randint
  8. class ResUsers(models.Model):
  9. _inherit = 'res.users'
  10. staff_ids = fields.One2many('staff', 'user_id', '关联员工')
  11. staff_id = fields.Many2one('staff', '公司员工',
  12. compute='_compute_staff_id',
  13. search='_search_company_staff',
  14. store=True,)
  15. staff_count = fields.Integer(compute='_compute_staff_count')
  16. @api.depends('staff_ids')
  17. def _compute_staff_id(self):
  18. for line in self:
  19. line.staff_id = False
  20. if line.staff_ids and len(line.staff_ids)==1:
  21. line.staff_id = line.staff_ids[0].id
  22. def _search_company_staff(self, operator, value):
  23. staff = self.env['staff'].search([
  24. ('name', operator, value),
  25. '|',
  26. ('company_id', '=', self.env.company.id),
  27. ('company_id', '=', False)
  28. ], order='company_id ASC')
  29. return [('id', 'in', staff.mapped('user_id').ids)]
  30. def action_create_staff(self):
  31. self.ensure_one()
  32. self.env['staff'].create(dict(
  33. name=self.name,
  34. company_id=self.env.company.id,
  35. **self.env['staff']._sync_user(self)
  36. ))
  37. @api.depends('staff_ids')
  38. def _compute_staff_count(self):
  39. for user in self.with_context(active_test=False):
  40. user.staff_count = len(user.staff_ids)
  41. class StaffDepartment(models.Model):
  42. _name = "staff.department"
  43. _description = '员工部门'
  44. _inherits = {'auxiliary.financing': 'auxiliary_id'}
  45. auxiliary_id = fields.Many2one(
  46. string='辅助核算',
  47. comodel_name='auxiliary.financing',
  48. ondelete='cascade',
  49. required=True,
  50. )
  51. dtype = fields.Selection(
  52. [("sell", "销售"), ("admin", "管理"), ("develop", "研发"), ("produce", "制造")],
  53. string='部门类别', default="admin", required=True)
  54. manager_id = fields.Many2one('staff', '部门经理')
  55. member_ids = fields.One2many('staff', 'department_id', '部门成员')
  56. parent_id = fields.Many2one('staff.department', '上级部门')
  57. child_ids = fields.One2many('staff.department', 'parent_id', '下级部门')
  58. jobs_ids = fields.One2many('staff.job', 'department_id', '职位')
  59. note = fields.Text('备注')
  60. active = fields.Boolean('启用', default=True)
  61. @api.constrains('parent_id')
  62. def _check_parent_id(selfs):
  63. '''上级部门不能选择自己和下级的部门'''
  64. for self in selfs:
  65. if self.parent_id:
  66. staffs = self.env['staff.department'].search(
  67. [('parent_id', '=', self.id)])
  68. if self.parent_id in staffs:
  69. raise UserError('上级部门不能选择他自己或者他的下级部门')
  70. def view_detail(self):
  71. for child_department in self:
  72. context = {'default_name': child_department.name,
  73. 'default_manager_id': child_department.manager_id.id,
  74. 'default_parent_id': child_department.parent_id.id}
  75. res_id = self.env['staff.department'].search(
  76. [('id', '=', child_department.id)])
  77. view_id = self.env.ref('staff.view_staff_department_form').id
  78. return {
  79. 'name': '部门/' + child_department.name,
  80. 'view_mode': 'form',
  81. 'res_model': 'staff.department',
  82. 'res_id': res_id.id,
  83. 'view_id': False,
  84. 'views': [(view_id, 'form')],
  85. 'type': 'ir.actions.act_window',
  86. 'context': context,
  87. 'target': 'current',
  88. }
  89. class StaffJob(models.Model):
  90. _name = "staff.job"
  91. _description = '员工职位'
  92. name = fields.Char('职位', required=True)
  93. note = fields.Text('描述')
  94. account_id = fields.Many2one('finance.account', '计提工资科目')
  95. department_id = fields.Many2one('staff.department', '部门')
  96. active = fields.Boolean('启用', default=True)
  97. company_id = fields.Many2one(
  98. 'res.company',
  99. string='公司',
  100. change_default=True,
  101. default=lambda self: self.env.company)
  102. _sql_constraints = [
  103. ('name_uniq', 'unique(name,department_id)', '同部门的职位不能重复!')
  104. ]
  105. class StaffEmployeeCategory(models.Model):
  106. _name = "staff.employee.category"
  107. _description = '员工层级'
  108. def _get_default_color(self):
  109. return randint(1, 11)
  110. name = fields.Char('名称',required=True)
  111. parent_id = fields.Many2one('staff.employee.category', '上级标签', index=True)
  112. chield_ids = fields.One2many(
  113. 'staff.employee.category', 'parent_id', '下级标签')
  114. employee_ids = fields.Many2many('staff',
  115. 'staff_employee_category_rel',
  116. 'category_id',
  117. 'emp_id', '员工')
  118. company_id = fields.Many2one(
  119. 'res.company',
  120. string='公司',
  121. change_default=True,
  122. default=lambda self: self.env.company)
  123. color = fields.Integer(string='分类颜色', default=_get_default_color)
  124. _sql_constraints = [
  125. ('name_uniq', 'unique (name)', "分类标签已经存在!"),
  126. ]
  127. class Staff(models.Model):
  128. _inherit = 'staff'
  129. _inherits = {'auxiliary.financing': 'auxiliary_id'}
  130. _order = 'department_id, work_no'
  131. @api.onchange('job_id')
  132. def onchange_job_id(self):
  133. '''选择职位时带出部门和部门经理'''
  134. if self.job_id:
  135. self.department_id = self.job_id.department_id
  136. self.parent_id = self.job_id.department_id.manager_id
  137. # @api.constrains('work_email')
  138. # def _check_work_email(selfs):
  139. # ''' 验证 work_email 合法性 '''
  140. # for self in selfs:
  141. # if self.work_email:
  142. # res = re.match('^[a-zA-Z0-9_-_.]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$', self.work_email)
  143. # if not res:
  144. # raise UserError('请检查邮箱格式是否正确: %s' % self.work_email)
  145. def _sync_user(self, user):
  146. vals = dict(
  147. image_medium=user.image_1920,
  148. work_email=user.email if user.email else '',
  149. user_id=user.id,
  150. )
  151. return vals
  152. auxiliary_id = fields.Many2one(
  153. string='辅助核算',
  154. comodel_name='auxiliary.financing',
  155. ondelete='restrict',
  156. required=True,
  157. )
  158. category_ids = fields.Many2many('staff.employee.category',
  159. 'staff_employee_category_rel',
  160. 'emp_id',
  161. 'category_id', string='标签')
  162. work_email = fields.Char(string='办公邮箱')
  163. work_phone = fields.Char(string='办公电话')
  164. image_medium = fields.Image(string='头像',attachment=True)
  165. # 个人信息
  166. birthday = fields.Date(string='生日')
  167. # identification_id = fields.Char(string='证照号码')
  168. is_arbeitnehmer = fields.Boolean(string='是否雇员', default='1')
  169. is_investoren = fields.Boolean(string='是否投资者')
  170. is_bsw = fields.Boolean(string='是否残疾烈属孤老')
  171. gender = fields.Selection([
  172. ('male', '男'),
  173. ('female', '女')
  174. ], string='性别')
  175. marital = fields.Selection([
  176. ('single', '单身'),
  177. ('married', '已婚'),
  178. ('widower', '丧偶'),
  179. ('divorced', '离异')
  180. ], string='婚姻状况')
  181. contract_ids = fields.One2many('staff.contract', 'staff_id', string='合同')
  182. active = fields.Boolean(string='启用', default=True)
  183. # 公开信息
  184. work_mobile = fields.Char(string='办公手机')
  185. department_id = fields.Many2one('staff.department', string='部门')
  186. parent_id = fields.Many2one('staff', string='部门经理')
  187. job_id = fields.Many2one('staff.job', string='职位')
  188. notes = fields.Text(string='其他信息')
  189. emergency_contact = fields.Char(string='紧急联系人')
  190. emergency_call = fields.Char(string='紧急联系方式')
  191. bank_name = fields.Char(string='工资卡号')
  192. bank_num = fields.Char(string='工资卡开户行')
  193. @api.model
  194. def staff_contract_over_date(self):
  195. # 员工合同到期,发送邮件给员工 和 部门经理(如果存在)
  196. now = datetime.now().strftime("%Y-%m-%d")
  197. for Staff in self.search([]):
  198. if not Staff.contract_ids:
  199. continue
  200. for contract in Staff.contract_ids:
  201. if now == contract.over_date:
  202. self.env.ref('staff.contract_over_due_date_employee').send_mail(
  203. self.env.user.id)
  204. if Staff.parent_id and Staff.parent_id.work_email:
  205. self.env.ref('staff.contract_over_due_date_manager').send_mail(
  206. self.env.user.id)
  207. return
  208. # ===========================
  209. # @Time : 2020/12/24 16:23
  210. # @Author : Jason Zou
  211. # @Email : zou.jason@qq.com
  212. # 以下扩展员工个人信息
  213. # ===========================
  214. work_no = fields.Char(string='员工工号')
  215. work_date = fields.Date(string='参加工作日期', )
  216. join_date = fields.Date(string='加入本司日期', )
  217. training_date = fields.Date(string='新员工培训日期', )
  218. confirm_date = fields.Date(string='转正日期', )
  219. spouse_complete_name = fields.Char(string='配偶全名', )
  220. spouse_birthdate = fields.Date(string='配偶生日', )
  221. km_home_work = fields.Char(string='家和公司之间的距离', )
  222. contract_category_id = fields.Many2one('core.value',
  223. string='合同类别',
  224. ondelete='restrict',
  225. domain=[('type', '=', 'contract_category')],
  226. context={'type': 'contract_category'})
  227. confident_agreement_id = fields.Many2one('core.value',
  228. string='保密协议类别',
  229. ondelete='restrict',
  230. domain=[('type', '=', 'confident_agreement')],
  231. context={'type': 'confident_agreement'})
  232. job_type_id = fields.Many2one('core.value',
  233. string='岗位类型',
  234. ondelete='restrict',
  235. domain=[('type', '=', 'job_type')],
  236. context={'type': 'job_type'})
  237. reimbursement_card = fields.Char(string='报销卡号', )
  238. account_reimbursement_card = fields.Char(string='报销卡开户行', )
  239. professional_title_id = fields.Many2one('core.value',
  240. string='职称',
  241. ondelete='restrict',
  242. domain=[('type', '=', 'professional_title')],
  243. context={'type': 'professional_title'})
  244. graduation_certificate_id = fields.Many2one('core.value',
  245. string='证书',
  246. ondelete='restrict',
  247. domain=[('type', '=', 'graduation_certificate')],
  248. context={'type': 'graduation_certificate'})
  249. children = fields.Char(string='子女数', )
  250. child_one = fields.Char(string='子女1姓名', )
  251. child_one_birthday = fields.Date(string='子女1出生日期', )
  252. child_two = fields.Char(string='子女2姓名', )
  253. child_two_birthday = fields.Date(string='子女2出生日期', )
  254. relationship_one_id = fields.Many2one('core.value',
  255. string='关系',
  256. ondelete='restrict',
  257. domain=[('type', '=', 'relationship_one')],
  258. context={'type': 'relationship_one'})
  259. second_contact = fields.Char(string='第二紧急联系人', )
  260. relationship_two_id = fields.Many2one('core.value',
  261. string='第二紧急联系人关系',
  262. ondelete='restrict',
  263. domain=[('type', '=', 'relationship_two')],
  264. context={'type': 'relationship_two'})
  265. second_contact_tel = fields.Char(string='第二紧急联系人电话', )
  266. archives_place = fields.Char(string='档案存放地', )
  267. leaving_reason_id = fields.Many2one('core.value',
  268. string='离职原因',
  269. ondelete='restrict',
  270. domain=[('type', '=', 'leaving_reason')],
  271. context={'type': 'leaving_reason'})
  272. last_working = fields.Date(string='最后工作日', )
  273. salary_date = fields.Date(string='工资结算日', )
  274. duration_agreement = fields.Char(string='培训协议期限', )
  275. political_outlook_id = fields.Many2one('core.value',
  276. string='政治面貌',
  277. ondelete='restrict',
  278. domain=[('type', '=', 'political_outlook')],
  279. context={'type': 'political_outlook'})
  280. nation_id = fields.Many2one('core.value',
  281. string='民族',
  282. ondelete='restrict',
  283. domain=[('type', '=', 'nation_ch')],
  284. context={'type': 'nation_ch'})
  285. native_place = fields.Char(string='籍贯', )
  286. household_registration_id = fields.Many2one('core.value',
  287. string='户籍类型',
  288. ondelete='restrict',
  289. domain=[('type', '=', 'household_registration')],
  290. context={'type': 'household_registration'})
  291. actual_residence = fields.Char(string='实际居住地住址', )
  292. highest_education_id = fields.Many2one('core.value',
  293. string='最高学历',
  294. ondelete='restrict',
  295. domain=[('type', '=', 'highest_education')],
  296. context={'type': 'highest_education'})
  297. major_title_id = fields.Many2one('core.value',
  298. string='专业',
  299. ondelete='restrict',
  300. domain=[('type', '=', 'major_title')],
  301. context={'type': 'major_title'})
  302. university_graduated_id = fields.Many2one('core.value',
  303. string='毕业院校',
  304. ondelete='restrict',
  305. domain=[('type', '=', 'university_graduated')],
  306. context={'type': 'university_graduated'})
  307. learning_form_id = fields.Many2one('core.value',
  308. string='学历性质',
  309. ondelete='restrict',
  310. domain=[('type', '=', 'learning_form')],
  311. context={'type': 'learning_form'})
  312. graduation_date = fields.Date(string='毕业时间', )
  313. social_payment_address_id = fields.Many2one('core.value',
  314. string='社保公积金缴纳地点',
  315. ondelete='restrict',
  316. domain=[('type', '=', 'social_payment_address')],
  317. context={'type': 'social_payment_address'})
  318. commercial_insurance = fields.Char(string='商保号', )
  319. social_security_account = fields.Char(string='社保账号', )
  320. provident_fund_account = fields.Char(string='公积金账号', )
  321. @api.model_create_multi
  322. def create(self, vals_list):
  323. for vals in vals_list:
  324. if vals.get('user_id'):
  325. user = self.env['res.users'].browse(vals['user_id'])
  326. vals.update(self._sync_user(user))
  327. vals['name'] = vals.get('name', user.name)
  328. return super().create(vals_list)
  329. def write(self, vals):
  330. if vals.get('user_id'):
  331. vals.update(self._sync_user(self.env['res.users'].browse(vals['user_id'])))
  332. return super().write(vals)
  333. def name_get(self):
  334. result = []
  335. for record in self:
  336. if record.work_no and record.name:
  337. result.append((record.id, '[' + record.work_no + ']' + record.name))
  338. elif record.name and (not record.work_no):
  339. result.append((record.id, record.name))
  340. return result
  341. @api.model
  342. def name_search(self, name, args=None, operator='ilike', limit=100):
  343. args = args or []
  344. staff_ids = []
  345. if name:
  346. staff_ids = self.search(['|', ('work_no', '=', name), ('name', '=', name)] + args, limit=limit)
  347. if not staff_ids:
  348. staff_ids = self.search(['|', ('work_no', operator, name), ('name', operator, name)] + args, limit=limit)
  349. return staff_ids.name_get()
  350. @api.depends('work_no', 'name')
  351. def _compute_display_name(self):
  352. for line in self:
  353. work_no_part = f"[{line.work_no}]" if line.work_no else ""
  354. name_part = line.name or ""
  355. line.display_name = f"{work_no_part}{name_part}"
上海开阖软件有限公司 沪ICP备12045867号-1