中国本土应用
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.

459 lines
16KB

  1. # -*- coding: utf-8 -*-
  2. import logging
  3. import json
  4. import time
  5. from odoo import fields, models, api, Command, tools, _
  6. from odoo.exceptions import UserError
  7. import xmltodict
  8. from odoo.addons.oec_im_wecom_api.api.wecom_abstract_api import ApiException
  9. from odoo.addons.base.models.ir_mail_server import MailDeliveryException
  10. _logger = logging.getLogger(__name__)
  11. class WecomDepartment(models.Model):
  12. _name = "wecom.department"
  13. _description = "Wecom department"
  14. _order = "complete_name"
  15. # 企微字段
  16. department_id = fields.Integer(
  17. string="Department ID", readonly=True, default=0
  18. ) # 部门id
  19. name = fields.Char(string="Name", readonly=True, default="",compute="_compute_name",) # 部门名称
  20. name_en = fields.Char(string="English name", readonly=True, default="") # 英部门文名称
  21. department_leader = fields.Char(
  22. string="Department Leader", readonly=True, default="[]"
  23. ) # 部门负责人的UserID;第三方仅通讯录应用可获取
  24. parentid = fields.Integer(
  25. string="Parent department id",
  26. readonly=True,
  27. default=0,
  28. domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
  29. ) # 父部门id。根部门为1
  30. order = fields.Integer(
  31. string="Sequence",
  32. readonly=True,
  33. default=0,
  34. ) # 在父部门中的次序值。order值大的排序靠前。值范围是[0, 2^32)
  35. # odoo字段
  36. company_id = fields.Many2one(
  37. "res.company",
  38. required=True,
  39. domain="[('is_wecom_organization', '=', True)]",
  40. copy=False,
  41. store=True,
  42. )
  43. parent_id = fields.Many2one(
  44. "wecom.department",
  45. string="Parent Department",
  46. index=True,
  47. domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
  48. store=True,
  49. compute="_compute_parent_id",
  50. )
  51. child_ids = fields.One2many(
  52. "wecom.department", "parentid", string="Child Departments"
  53. )
  54. complete_name = fields.Char(
  55. "Complete Name", compute="_compute_complete_name", recursive=True, store=True
  56. )
  57. department_leader_ids = fields.Many2many(
  58. "wecom.user",
  59. "user_department_rel",
  60. "tag_id",
  61. "user_id",
  62. string="Department Leader",
  63. )
  64. user_ids = fields.Many2many(
  65. "wecom.user",
  66. "wecom_user_department_rel",
  67. "department_id",
  68. "user_id",
  69. string="Members",
  70. )
  71. color = fields.Integer("Color Index")
  72. @api.depends("department_id","company_id")
  73. def _compute_name(self):
  74. for department in self:
  75. if not department.name:
  76. department.name = "%s:%s" % (department.company_id.name, department.department_id)
  77. @api.depends("parentid","company_id")
  78. def _compute_parent_id(self):
  79. for department in self:
  80. if department.parentid:
  81. parent_department = self.sudo().search(
  82. [
  83. ("department_id", "=", department.parentid),("company_id", "=", department.company_id.id)
  84. ]
  85. )
  86. department.parent_id = parent_department.id
  87. @api.depends("name", "parent_id","company_id")
  88. def _compute_complete_name(self):
  89. for department in self:
  90. if department.parent_id:
  91. department.complete_name = "%s / %s" % (
  92. department.parent_id.complete_name,
  93. department.name,
  94. )
  95. else:
  96. department.complete_name = department.name
  97. # @api.onchange('parent_id')
  98. # def _onchange_parentid(self):
  99. # for department in self:
  100. # if department.parent_id:
  101. # department.complete_name = "%s / %s" % (
  102. # department.parent_id.complete_name,
  103. # department.name,
  104. # )
  105. # ------------------------------------------------------------
  106. # 企微部门下载
  107. # ------------------------------------------------------------
  108. @api.model
  109. def download_wecom_deps(self):
  110. """
  111. 下载部门列表
  112. """
  113. start_time = time.time()
  114. company = self.env.context.get("company_id")
  115. if type(company) == int:
  116. company = self.env["res.company"].browse(company)
  117. tasks = []
  118. try:
  119. wxapi = self.env["wecom.service_api"].InitServiceApi(
  120. company.corpid, company.contacts_app_id.secret
  121. )
  122. # 2022-08-10 按官方建议进行重构
  123. # 官方建议换用 获取子部门ID列表 与 获取单个部门详情 组合的方式获取部门
  124. response = wxapi.httpCall(
  125. self.env["wecom.service_api_list"].get_server_api_call(
  126. "DEPARTMENT_SIMPLELIST"
  127. ),
  128. )
  129. except ApiException as ex:
  130. end_time = time.time()
  131. self.env["wecomapi.tools.action"].ApiExceptionDialog(
  132. ex, raise_exception=False
  133. )
  134. tasks = [
  135. {
  136. "name": "download_department_data",
  137. "state": False,
  138. "time": end_time - start_time,
  139. "msg": str(ex),
  140. }
  141. ]
  142. except Exception as e:
  143. end_time = time.time()
  144. tasks = [
  145. {
  146. "name": "download_department_data",
  147. "state": False,
  148. "time": end_time - start_time,
  149. "msg": str(e),
  150. }
  151. ]
  152. else:
  153. if response["errcode"] == 0:
  154. # 获取只有 'id' , 'parentid' , 'order' 字段的列表
  155. wecom_departments = response["department_id"]
  156. # 1.下载部门
  157. for wecom_department in wecom_departments:
  158. download_department_result = self.download_department(
  159. company, wecom_department
  160. )
  161. if download_department_result:
  162. for r in download_department_result:
  163. tasks.append(r) # 加入 下载员工失败结果
  164. # 3.完成
  165. end_time = time.time()
  166. task = {
  167. "name": "download_department_data",
  168. "state": True,
  169. "time": end_time - start_time,
  170. "msg": _("Department list sync completed."),
  171. }
  172. tasks.append(task)
  173. finally:
  174. return tasks # 返回结果
  175. def download_department(self, company, wecom_department):
  176. """
  177. 下载部门
  178. """
  179. # 查询数据库是否存在相同的企业微信部门ID,有则更新,无则新建
  180. department = self.sudo().search(
  181. [
  182. ("department_id", "=", wecom_department["id"]),
  183. ("company_id", "=", company.id),
  184. ],
  185. limit=1,
  186. )
  187. result = {}
  188. if not department:
  189. result = self.create_department(company, department, wecom_department)
  190. else:
  191. result = self.update_department(company, department, wecom_department)
  192. return result
  193. def create_department(self, company, department, wecom_department):
  194. """
  195. 创建部门
  196. """
  197. try:
  198. department.create(
  199. {
  200. "department_id": wecom_department["id"],
  201. "parentid": wecom_department["parentid"],
  202. "order": wecom_department["order"],
  203. "company_id": company.id,
  204. }
  205. )
  206. except Exception as e:
  207. result = _(
  208. "Error creating company [%s]'s department [%s], error reason: %s"
  209. ) % (
  210. company.name,
  211. wecom_department["id"],
  212. repr(e),
  213. )
  214. _logger.warning(result)
  215. return {
  216. "name": "add_department",
  217. "state": False,
  218. "time": 0,
  219. "msg": result,
  220. }
  221. def update_department(self, company, department, wecom_department):
  222. """
  223. 更新部门
  224. """
  225. try:
  226. department.write(
  227. {
  228. "parentid": wecom_department["parentid"],
  229. "order": wecom_department["order"],
  230. }
  231. )
  232. except Exception as e:
  233. result = _("Error updating Department [%s], error details:%s") % (
  234. wecom_department["name"],
  235. str(e),
  236. )
  237. result = _(
  238. "Error update company [%s]'s Department [%s], error reason: %s"
  239. ) % (
  240. company.name,
  241. wecom_department["id"],
  242. repr(e),
  243. )
  244. _logger.warning(result)
  245. return {
  246. "name": "update_department",
  247. "state": False,
  248. "time": 0,
  249. "msg": result,
  250. }
  251. def set_parent_department(self, company):
  252. """[summary]
  253. 由于json数据是无序的,故在同步到本地数据库后,需要设置新增企业微信部门的上级部门
  254. """
  255. params = self.env["ir.config_parameter"].sudo()
  256. debug = params.get_param("wecom.debug_enabled")
  257. departments = self.search([("company_id", "=", company.id)])
  258. results = []
  259. for department in departments:
  260. if department.parentid and department.parentid != 0:
  261. # 忽略 parentid 为 0的部门
  262. parent_department = self.get_parent_department_by_department_id(
  263. department, company
  264. )
  265. try:
  266. department.write(
  267. {
  268. "parent_id": parent_department.id,
  269. }
  270. )
  271. except Exception as e:
  272. result = _(
  273. "Error setting parent department for company [%s], Error details:%s"
  274. ) % (company.name, repr(e))
  275. if debug:
  276. _logger.warning(result)
  277. results.append(
  278. {
  279. "name": "set_parent_department",
  280. "state": False,
  281. "time": 0,
  282. "msg": result,
  283. }
  284. )
  285. return results # 返回失败的结果
  286. def get_parent_department_by_department_id(self, department, company):
  287. """[summary]
  288. 通过企微部门id 获取上级部门
  289. Args:
  290. department ([type]): [description]
  291. departments ([type]): [descriptions]
  292. """
  293. parent_department = self.search(
  294. [
  295. ("department_id", "=", department.parentid),
  296. ("company_id", "=", company.id),
  297. ]
  298. )
  299. return parent_department
  300. def download_single_department(self):
  301. """
  302. 下载单个部门
  303. """
  304. company = self.company_id
  305. params = {}
  306. message = ""
  307. try:
  308. wxapi = self.env["wecom.service_api"].InitServiceApi(
  309. company.corpid, company.contacts_app_id.secret
  310. )
  311. response = wxapi.httpCall(
  312. self.env["wecom.service_api_list"].get_server_api_call(
  313. "DEPARTMENT_DETAILS"
  314. ),
  315. {"id": str(self.department_id)},
  316. )
  317. department = response["department"]
  318. for key in department.keys():
  319. if type(department[key]) in (list, dict) and department[key]:
  320. json_str = json.dumps(
  321. department[key],
  322. sort_keys=False,
  323. indent=2,
  324. separators=(",", ":"),
  325. ensure_ascii=False,
  326. )
  327. department[key] = json_str
  328. self.sudo().write(
  329. {
  330. "name": department["name"],
  331. "name_en": self.env["wecomapi.tools.dictionary"].check_dictionary_keywords(
  332. department, "name_en"
  333. ),
  334. "department_leader": department["department_leader"],
  335. "parentid": department["parentid"],
  336. "order": department["order"],
  337. }
  338. )
  339. except ApiException as ex:
  340. message = _("Department [id:%s, name:%s] failed to download,Reason: %s") % (
  341. self.department_id,
  342. self.name,
  343. str(ex),
  344. )
  345. _logger.warning(message)
  346. params = {
  347. "title": _("Download failed!"),
  348. "message": message,
  349. "sticky": True, # 延时关闭
  350. "className": "bg-danger",
  351. "type": "danger",
  352. }
  353. except Exception as e:
  354. message = _("Department [id:%s, name:%s] failed to download,Reason: %s") % (
  355. self.department_id,
  356. self.name,
  357. str(e),
  358. )
  359. _logger.warning(message)
  360. params = {
  361. "title": _("Download failed!"),
  362. "message": message,
  363. "sticky": True, # 延时关闭
  364. "className": "bg-danger",
  365. "type": "danger",
  366. }
  367. else:
  368. message = _("Department [id:%s, name:%s] downloaded successfully") % (
  369. self.department_id,
  370. self.name,
  371. )
  372. params = {
  373. "title": _("Download Success!"),
  374. "message": message,
  375. "sticky": False, # 延时关闭
  376. "className": "bg-success",
  377. "type": "success",
  378. "next": {
  379. "type": "ir.actions.client",
  380. "tag": "reload",
  381. }, # 刷新窗体
  382. }
  383. finally:
  384. action = {
  385. "type": "ir.actions.client",
  386. "tag": "display_notification",
  387. "params": params,
  388. }
  389. return action
  390. # ------------------------------------------------------------
  391. # 企微通讯录事件
  392. # ------------------------------------------------------------
  393. def wecom_event_change_contact_party(self, cmd):
  394. xml_tree = self.env.context.get("xml_tree")
  395. company_id = self.env.context.get("company_id")
  396. department_dict = xmltodict.parse(xml_tree)["xml"]
  397. departments = self.sudo().search([("company_id", "=", company_id.id)])
  398. callback_department = departments.search(
  399. [("department_id", "=", department_dict["Id"])],
  400. limit=1,
  401. )
  402. update_dict = {}
  403. for key, value in department_dict.items():
  404. if key.lower() in self._fields.keys():
  405. update_dict.update({key.lower(): value})
  406. else:
  407. if key == "Id":
  408. update_dict.update({"department_id": value})
  409. if "parentid" in update_dict:
  410. parent_id = departments.search(
  411. [("department_id", "=", update_dict["parentid"])],
  412. limit=1,
  413. ).id
  414. update_dict.update({"parent_id": parent_id})
  415. if cmd == "create":
  416. callback_department.create(update_dict)
  417. elif cmd == "update":
  418. if "department_id" in update_dict:
  419. del update_dict["department_id"]
  420. callback_department.write(update_dict)
  421. elif cmd == "delete":
  422. callback_department.unlink()
上海开阖软件有限公司 沪ICP备12045867号-1