198 lines
7.8 KiB
Python
198 lines
7.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
import json
|
|
|
|
from odoo import http
|
|
from odoo.exceptions import AccessError
|
|
from odoo.http import request, Response
|
|
|
|
|
|
def _json(data, status=200):
|
|
return Response(json.dumps(data, ensure_ascii=False),
|
|
mimetype='application/json', status=status)
|
|
|
|
|
|
def _body():
|
|
try:
|
|
return json.loads(request.httprequest.get_data(as_text=True) or '{}')
|
|
except (ValueError, TypeError):
|
|
return {}
|
|
|
|
|
|
class BpdController(http.Controller):
|
|
"""业务流程图前端的数据接口。
|
|
|
|
读图对所有登录用户开放;新建节点 / 连线 / 移动 / 改名 / 删除 等写操作
|
|
只有系统管理员有 write/create 权限(权限由 ir.model.access.csv 控制),
|
|
普通用户会收到 403,前端据此提示「需管理员权限」。
|
|
"""
|
|
|
|
# ---------------- 只读:拿单张图 / 图列表 ----------------
|
|
@http.route('/yuthon_bpd/api/diagram/data', type='http',
|
|
auth='user', methods=['GET'], csrf=False)
|
|
def api_diagram_data(self, **kw):
|
|
"""不带 id:返回默认图(key=erp_mto);带 ?id=N:返回指定图。"""
|
|
Diagram = request.env['yuthon.bpd.diagram']
|
|
did = kw.get('id')
|
|
if did not in (None, '', '0'):
|
|
try:
|
|
did = int(did)
|
|
except (TypeError, ValueError):
|
|
return _json({'error': 'bad_id'}, 400)
|
|
rec = Diagram.browse(did).exists()
|
|
if not rec:
|
|
return _json({'error': 'not_found'}, 404)
|
|
return _json({'diagram': rec.to_json()})
|
|
data = Diagram.get_default()
|
|
if not data:
|
|
return _json({'error': 'no_diagram'}, 404)
|
|
return _json({'diagram': data})
|
|
|
|
@http.route('/yuthon_bpd/api/diagram/list', type='http',
|
|
auth='user', methods=['GET'], csrf=False)
|
|
def api_diagram_list(self, **kw):
|
|
"""供前端下拉切换:返回所有流程图的 id / 名称 / key。"""
|
|
recs = request.env['yuthon.bpd.diagram'].search([], order='id')
|
|
return _json({'diagrams': [
|
|
{'id': r.id, 'name': r.name, 'key': r.key or ''} for r in recs
|
|
]})
|
|
|
|
# ---------------- 写:节点 ----------------
|
|
@http.route('/yuthon_bpd/api/diagram/<int:did>/node', type='http',
|
|
auth='user', methods=['POST'], csrf=False)
|
|
def api_create_node(self, did, **kw):
|
|
diagram = request.env['yuthon.bpd.diagram'].browse(did).exists()
|
|
if not diagram:
|
|
return _json({'error': 'not_found'}, 404)
|
|
p = _body()
|
|
label = (p.get('label') or '').strip()[:120]
|
|
if not label:
|
|
return _json({'error': 'empty_label'}, 400)
|
|
lane_id = p.get('lane_id') or False
|
|
try:
|
|
node = request.env['yuthon.bpd.node'].create({
|
|
'diagram_id': did,
|
|
'lane_id': int(lane_id) if lane_id else False,
|
|
'label': label,
|
|
'sub': (p.get('sub') or '').strip()[:60],
|
|
'x': int(p.get('x', 0) or 0),
|
|
'y': int(p.get('y', 0) or 0),
|
|
'w': int(p.get('w', 96) or 96),
|
|
'h': int(p.get('h', 34) or 34),
|
|
'shape': p.get('shape') or 'box',
|
|
'variant': p.get('variant') or 'doc',
|
|
})
|
|
except AccessError:
|
|
return _json({'error': 'forbidden'}, 403)
|
|
except Exception as exc:
|
|
if 'unique' in str(exc).lower():
|
|
return _json({'error': 'duplicate'}, 409)
|
|
raise
|
|
return _json({'node': node.to_json()})
|
|
|
|
@http.route('/yuthon_bpd/api/node/<int:nid>/move', type='http',
|
|
auth='user', methods=['POST'], csrf=False)
|
|
def api_move_node(self, nid, **kw):
|
|
node = request.env['yuthon.bpd.node'].browse(nid).exists()
|
|
if not node:
|
|
return _json({'error': 'not_found'}, 404)
|
|
p = _body()
|
|
try:
|
|
node.write({
|
|
'x': int(p.get('x', node.x)),
|
|
'y': int(p.get('y', node.y)),
|
|
})
|
|
except AccessError:
|
|
return _json({'error': 'forbidden'}, 403)
|
|
return _json({'ok': True})
|
|
|
|
@http.route('/yuthon_bpd/api/node/<int:nid>/rename', type='http',
|
|
auth='user', methods=['POST'], csrf=False)
|
|
def api_rename_node(self, nid, **kw):
|
|
node = request.env['yuthon.bpd.node'].browse(nid).exists()
|
|
if not node:
|
|
return _json({'error': 'not_found'}, 404)
|
|
p = _body()
|
|
label = (p.get('label') or '').strip()[:120]
|
|
if not label:
|
|
return _json({'error': 'empty_label'}, 400)
|
|
try:
|
|
node.write({'label': label})
|
|
except AccessError:
|
|
return _json({'error': 'forbidden'}, 403)
|
|
return _json({'ok': True, 'label': node.label})
|
|
|
|
# ---------------- 写:连线 ----------------
|
|
@http.route('/yuthon_bpd/api/diagram/<int:did>/edge', type='http',
|
|
auth='user', methods=['POST'], csrf=False)
|
|
def api_create_edge(self, did, **kw):
|
|
diagram = request.env['yuthon.bpd.diagram'].browse(did).exists()
|
|
if not diagram:
|
|
return _json({'error': 'not_found'}, 404)
|
|
p = _body()
|
|
try:
|
|
frm = int(p.get('from'))
|
|
to = int(p.get('to'))
|
|
except (TypeError, ValueError):
|
|
return _json({'error': 'bad_params'}, 400)
|
|
if frm == to:
|
|
return _json({'error': 'same_node'}, 400)
|
|
Node = request.env['yuthon.bpd.node']
|
|
if not (Node.browse(frm).exists() and Node.browse(to).exists()):
|
|
return _json({'error': 'node_missing'}, 404)
|
|
try:
|
|
edge = request.env['yuthon.bpd.edge'].create({
|
|
'diagram_id': did,
|
|
'from_id': frm,
|
|
'to_id': to,
|
|
'label': (p.get('label') or '').strip()[:60],
|
|
'style': p.get('style') or 'solid',
|
|
})
|
|
except AccessError:
|
|
return _json({'error': 'forbidden'}, 403)
|
|
except Exception as exc:
|
|
if 'unique' in str(exc).lower():
|
|
return _json({'error': 'duplicate'}, 409)
|
|
raise
|
|
return _json({'edge': edge.to_json()})
|
|
|
|
@http.route('/yuthon_bpd/api/edge/<int:eid>/delete', type='http',
|
|
auth='user', methods=['POST'], csrf=False)
|
|
def api_delete_edge(self, eid, **kw):
|
|
edge = request.env['yuthon.bpd.edge'].browse(eid).exists()
|
|
if not edge:
|
|
return _json({'error': 'not_found'}, 404)
|
|
try:
|
|
edge.unlink()
|
|
except AccessError:
|
|
return _json({'error': 'forbidden'}, 403)
|
|
return _json({'ok': True})
|
|
|
|
# ---------------- 写:泳道 ----------------
|
|
@http.route('/yuthon_bpd/api/diagram/<int:did>/lane', type='http',
|
|
auth='user', methods=['POST'], csrf=False)
|
|
def api_create_lane(self, did, **kw):
|
|
diagram = request.env['yuthon.bpd.diagram'].browse(did).exists()
|
|
if not diagram:
|
|
return _json({'error': 'not_found'}, 404)
|
|
p = _body()
|
|
name = (p.get('name') or '新泳道').strip()[:60]
|
|
try:
|
|
lane = request.env['yuthon.bpd.lane'].create({
|
|
'diagram_id': did,
|
|
'name': name,
|
|
'kind': p.get('kind') or 'column',
|
|
'x': int(p.get('x', 24) or 24),
|
|
'y': int(p.get('y', 86) or 86),
|
|
'w': int(p.get('w', 300) or 300),
|
|
'h': int(p.get('h', 600) or 600),
|
|
'head_h': int(p.get('head_h', 34) or 34),
|
|
'label_pos': p.get('label_pos') or 'top-center',
|
|
})
|
|
except AccessError:
|
|
return _json({'error': 'forbidden'}, 403)
|
|
return _json({'lane': {
|
|
'id': lane.id, 'name': lane.name, 'kind': lane.kind,
|
|
'x': lane.x, 'y': lane.y, 'w': lane.w, 'h': lane.h,
|
|
'head_h': lane.head_h, 'label_pos': lane.label_pos,
|
|
}})
|