|
|
@@ -0,0 +1,380 @@
|
|
|
+'use strict';
|
|
|
+
|
|
|
+const assert = require('node:assert/strict');
|
|
|
+const test = require('node:test');
|
|
|
+const moment = require('moment');
|
|
|
+const makeController = require('../../../app/controller/weapp_contract_controller');
|
|
|
+
|
|
|
+test('合同目录使用 UUID 子项目 ID 查询,不转换为数字', async () => {
|
|
|
+ const spid = '5472d997-c4ae-4421-8265-feb6ab0833db';
|
|
|
+ const projectId = '06c6dd13-e52b-4c37-9d90-468cf32942aa';
|
|
|
+ const calls = [];
|
|
|
+ const ctx = {
|
|
|
+ query: { spid, type: 'expenses' },
|
|
|
+ session: {
|
|
|
+ sessionProject: { id: projectId },
|
|
|
+ sessionUser: { is_admin: true },
|
|
|
+ },
|
|
|
+ service: {
|
|
|
+ subProject: {
|
|
|
+ getDataById: async id => {
|
|
|
+ calls.push([ 'subProject', id ]);
|
|
|
+ return { id: spid, project_id: projectId, page_show: '{}' };
|
|
|
+ },
|
|
|
+ getPageShow: () => ({ openContract: true }),
|
|
|
+ },
|
|
|
+ subProjPermission: { adminPermission: {} },
|
|
|
+ contractTree: {
|
|
|
+ insertTree: async options => calls.push([ 'insertTree', options ]),
|
|
|
+ getAllDataByCondition: async options => {
|
|
|
+ calls.push([ 'folders', options ]);
|
|
|
+ return [{ contract_id: 1, contract_pid: -1, name: '第一部分' }];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ contract: {
|
|
|
+ getListByUsers: async options => {
|
|
|
+ calls.push([ 'contracts', options ]);
|
|
|
+ return [{ id: 'contract-row-id', contract_id: 2, contract_pid: 1, name: '测试合同' }];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ contractAtt: {
|
|
|
+ getCounts: async ids => {
|
|
|
+ calls.push([ 'files', ids ]);
|
|
|
+ return new Map([[ 'contract-row-id', 2 ]]);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const Controller = makeController({ BaseController: class {} });
|
|
|
+ await Controller.prototype.catalog.call({ log: () => {} }, ctx);
|
|
|
+
|
|
|
+ assert.equal(ctx.body.code, 0);
|
|
|
+ assert.deepEqual(calls[0], [ 'subProject', spid ]);
|
|
|
+ assert.deepEqual(calls[1], [ 'insertTree', { spid } ]);
|
|
|
+ assert.deepEqual(calls[2][1].where, { spid, contract_type: 1 });
|
|
|
+ assert.deepEqual(calls[4], [ 'files', [ 'contract-row-id' ] ]);
|
|
|
+ assert.equal(ctx.body.data.nodes[1].fileCount, 2);
|
|
|
+});
|
|
|
+
|
|
|
+test('标段合同目录使用 tid 查询,并校验当前项目和合同成员', async () => {
|
|
|
+ const spid = '5472d997-c4ae-4421-8265-feb6ab0833db';
|
|
|
+ const projectId = '06c6dd13-e52b-4c37-9d90-468cf32942aa';
|
|
|
+ const calls = [];
|
|
|
+ const ctx = {
|
|
|
+ query: { spid, tid: '4852', type: 'income' },
|
|
|
+ session: {
|
|
|
+ sessionProject: { id: projectId },
|
|
|
+ sessionUser: { is_admin: false, accountId: 'user-id' },
|
|
|
+ },
|
|
|
+ service: {
|
|
|
+ subProject: {
|
|
|
+ getDataById: async () => ({ id: spid, project_id: projectId, page_show: '{}' }),
|
|
|
+ getPageShow: () => ({ openTenderContract: true }),
|
|
|
+ },
|
|
|
+ subProjPermission: {
|
|
|
+ getSubProjectUserPermission: async () => ({ contract_permission: [] }),
|
|
|
+ },
|
|
|
+ tender: {
|
|
|
+ getDataById: async id => {
|
|
|
+ calls.push([ 'tender', id ]);
|
|
|
+ return { id, spid, project_id: projectId };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ contractAudit: {
|
|
|
+ getDataByCondition: async options => {
|
|
|
+ calls.push([ 'audit', options ]);
|
|
|
+ return { id: 'audit-id' };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ contractTree: {
|
|
|
+ insertTree: async options => calls.push([ 'insertTree', options ]),
|
|
|
+ getAllDataByCondition: async options => {
|
|
|
+ calls.push([ 'folders', options ]);
|
|
|
+ return [];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ contract: { getListByUsers: async () => [] },
|
|
|
+ contractAtt: { getCounts: async () => new Map() },
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const Controller = makeController({ BaseController: class {} });
|
|
|
+ await Controller.prototype.catalog.call({ log: () => {} }, ctx);
|
|
|
+
|
|
|
+ assert.equal(ctx.body.code, 0);
|
|
|
+ assert.deepEqual(calls[0], [ 'tender', 4852 ]);
|
|
|
+ assert.deepEqual(calls[1], [ 'audit', { tid: 4852, uid: 'user-id' } ]);
|
|
|
+ assert.deepEqual(calls[2], [ 'insertTree', { tid: 4852 } ]);
|
|
|
+ assert.deepEqual(calls[3][1].where, { tid: 4852, contract_type: 2 });
|
|
|
+});
|
|
|
+
|
|
|
+test('标段合同选择列表只返回当前子项目的合同标段', async () => {
|
|
|
+ const spid = '5472d997-c4ae-4421-8265-feb6ab0833db';
|
|
|
+ const projectId = '06c6dd13-e52b-4c37-9d90-468cf32942aa';
|
|
|
+ const ctx = {
|
|
|
+ query: { spid },
|
|
|
+ session: {
|
|
|
+ sessionProject: { id: projectId },
|
|
|
+ sessionUser: { is_admin: true, accountId: 'user-id' },
|
|
|
+ },
|
|
|
+ service: {
|
|
|
+ subProject: {
|
|
|
+ getDataById: async () => ({ id: spid, project_id: projectId, page_show: '{}' }),
|
|
|
+ getPageShow: () => ({ openTenderContract: true }),
|
|
|
+ },
|
|
|
+ projectAccount: { getDataById: async () => ({ self_category_level: '' }) },
|
|
|
+ tender: {
|
|
|
+ getContractList: async () => {
|
|
|
+ assert.equal(ctx.subProject.id, spid);
|
|
|
+ return [{ id: 4852, name: '测试标段' }];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ category: { getAllCategory: async () => [] },
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const Controller = makeController({ BaseController: class {} });
|
|
|
+ await Controller.prototype.tenderList.call({ log: () => {} }, ctx);
|
|
|
+
|
|
|
+ assert.equal(ctx.body.code, 0);
|
|
|
+ assert.deepEqual(ctx.body.data.tenderList, [{ id: 4852, name: '测试标段' }]);
|
|
|
+});
|
|
|
+
|
|
|
+test('标段合同概况按 tid 汇总金额,并补齐近六个月趋势', async () => {
|
|
|
+ const spid = '5472d997-c4ae-4421-8265-feb6ab0833db';
|
|
|
+ const projectId = '06c6dd13-e52b-4c37-9d90-468cf32942aa';
|
|
|
+ const scopes = [];
|
|
|
+ const ctx = {
|
|
|
+ query: { spid, tid: '4852' },
|
|
|
+ session: {
|
|
|
+ sessionProject: { id: projectId },
|
|
|
+ sessionUser: { is_admin: true, accountId: 'user-id' },
|
|
|
+ },
|
|
|
+ helper: {
|
|
|
+ add: (a, b) => Number(a) + Number(b),
|
|
|
+ sub: (a, b) => Number(a) - Number(b),
|
|
|
+ div: (a, b) => Number(a) / Number(b),
|
|
|
+ round: (a, digits) => Number(a.toFixed(digits)),
|
|
|
+ },
|
|
|
+ service: {
|
|
|
+ subProject: {
|
|
|
+ getDataById: async () => ({ id: spid, project_id: projectId, page_show: '{}' }),
|
|
|
+ getPageShow: () => ({ openTenderContract: true }),
|
|
|
+ },
|
|
|
+ subProjPermission: { adminPermission: {} },
|
|
|
+ tender: { getDataById: async () => ({ id: 4852, spid, project_id: projectId }) },
|
|
|
+ contract: {
|
|
|
+ getListByUsers: async options => {
|
|
|
+ scopes.push(options);
|
|
|
+ return options.contract_type === 1
|
|
|
+ ? [
|
|
|
+ { id: 'expense-contract', total_price: 1000, yf_price: 250, sf_price: 180 },
|
|
|
+ { id: 'expense-contract', total_price: 1000, yf_price: 250, sf_price: 180 },
|
|
|
+ ]
|
|
|
+ : [{ id: 'income-contract', total_price: 2000, yf_price: 500, sf_price: 400 }];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ contractPay: {
|
|
|
+ getMonthlyYfPrice: async ids => [{
|
|
|
+ month: moment().format('YYYY-MM'),
|
|
|
+ amount: ids[0] === 'expense-contract' ? 100 : 200,
|
|
|
+ }],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const Controller = makeController({ BaseController: class {} });
|
|
|
+ await Controller.prototype.summary.call({ log: () => {} }, ctx);
|
|
|
+
|
|
|
+ assert.equal(ctx.body.code, 0);
|
|
|
+ assert.deepEqual(scopes, [
|
|
|
+ { tid: 4852, contract_type: 1 },
|
|
|
+ { tid: 4852, contract_type: 2 },
|
|
|
+ ]);
|
|
|
+ assert.deepEqual(ctx.body.data.expenses, {
|
|
|
+ total: 1000, paid: 180, unpaid: 820, percent: 25,
|
|
|
+ trend: [0, 0, 0, 0, 0, 100],
|
|
|
+ });
|
|
|
+ assert.deepEqual(ctx.body.data.income, {
|
|
|
+ total: 2000, paid: 400, unpaid: 1600, percent: 25,
|
|
|
+ trend: [0, 0, 0, 0, 0, 200],
|
|
|
+ });
|
|
|
+ assert.equal(ctx.body.data.months.length, 6);
|
|
|
+});
|
|
|
+
|
|
|
+test('项目合同概况按 spid 查询,空合同返回零值', async () => {
|
|
|
+ const spid = '5472d997-c4ae-4421-8265-feb6ab0833db';
|
|
|
+ const projectId = '06c6dd13-e52b-4c37-9d90-468cf32942aa';
|
|
|
+ const scopes = [];
|
|
|
+ const ctx = {
|
|
|
+ query: { spid },
|
|
|
+ session: {
|
|
|
+ sessionProject: { id: projectId },
|
|
|
+ sessionUser: { is_admin: true },
|
|
|
+ },
|
|
|
+ helper: { add: (a, b) => Number(a) + Number(b), sub: (a, b) => Number(a) - Number(b) },
|
|
|
+ service: {
|
|
|
+ subProject: {
|
|
|
+ getDataById: async () => ({ id: spid, project_id: projectId, page_show: '{}' }),
|
|
|
+ getPageShow: () => ({ openContract: true }),
|
|
|
+ },
|
|
|
+ subProjPermission: { adminPermission: {} },
|
|
|
+ contract: {
|
|
|
+ getListByUsers: async options => {
|
|
|
+ scopes.push(options);
|
|
|
+ return [];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ contractPay: { getMonthlyYfPrice: async () => [] },
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const Controller = makeController({ BaseController: class {} });
|
|
|
+ await Controller.prototype.summary.call({ log: () => {} }, ctx);
|
|
|
+
|
|
|
+ assert.equal(ctx.body.code, 0);
|
|
|
+ assert.deepEqual(scopes, [
|
|
|
+ { spid, contract_type: 1 },
|
|
|
+ { spid, contract_type: 2 },
|
|
|
+ ]);
|
|
|
+ assert.deepEqual(ctx.body.data.expenses, {
|
|
|
+ total: 0, paid: 0, unpaid: 0, percent: 0,
|
|
|
+ trend: [0, 0, 0, 0, 0, 0],
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+test('合同详情只返回当前可见合同的支付与文件', async () => {
|
|
|
+ const spid = '5472d997-c4ae-4421-8265-feb6ab0833db';
|
|
|
+ const projectId = '06c6dd13-e52b-4c37-9d90-468cf32942aa';
|
|
|
+ const queries = [];
|
|
|
+ const ctx = {
|
|
|
+ query: { spid, type: 'expenses', cid: 'contract-row-id' },
|
|
|
+ session: {
|
|
|
+ sessionProject: { id: projectId },
|
|
|
+ sessionUser: { is_admin: true },
|
|
|
+ },
|
|
|
+ service: {
|
|
|
+ subProject: {
|
|
|
+ getDataById: async () => ({ id: spid, project_id: projectId, page_show: '{}' }),
|
|
|
+ getPageShow: () => ({ openContract: true }),
|
|
|
+ },
|
|
|
+ subProjPermission: { adminPermission: {} },
|
|
|
+ contract: {
|
|
|
+ getListByUsers: async () => [{
|
|
|
+ id: 'contract-row-id', name: '测试合同', total_price: 1000,
|
|
|
+ pay_price: 800, debit_price: 50, yf_price: 750, sf_price: 500,
|
|
|
+ party_a_user: '甲方签约人', party_b_user: '乙方签约人',
|
|
|
+ entity: '收款单位', settle_code: 'JS-001',
|
|
|
+ }],
|
|
|
+ },
|
|
|
+ contractPay: {
|
|
|
+ getAllDataByCondition: async options => {
|
|
|
+ queries.push(options.where);
|
|
|
+ return [{ id: 1, pay_type: '网上转账', sf_price: 500, pay_time: '2025-12-06' }];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ contractAtt: {
|
|
|
+ getAllDataByCondition: async options => {
|
|
|
+ queries.push(options.where);
|
|
|
+ return [{ id: 2, filename: '合同.pdf', filesize: '1.34 MB', filepath: 'private/file.pdf' }];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const Controller = makeController({ BaseController: class {} });
|
|
|
+ await Controller.prototype.detail.call({ log: () => {} }, ctx);
|
|
|
+
|
|
|
+ assert.equal(ctx.body.code, 0);
|
|
|
+ assert.deepEqual(queries, [
|
|
|
+ { spid, contract_type: 1, cid: 'contract-row-id' },
|
|
|
+ { spid, contract_type: 1, cid: 'contract-row-id' },
|
|
|
+ ]);
|
|
|
+ assert.equal(ctx.body.data.pays[0].sf_price, 500);
|
|
|
+ assert.equal(ctx.body.data.contract.pay_price, 800);
|
|
|
+ assert.equal(ctx.body.data.contract.yf_price, 750);
|
|
|
+ assert.equal(ctx.body.data.contract.party_a_user, '甲方签约人');
|
|
|
+ assert.equal(ctx.body.data.contract.entity, '收款单位');
|
|
|
+ assert.equal(ctx.body.data.contract.settle_code, 'JS-001');
|
|
|
+ assert.equal(ctx.body.data.files[0].filename, '合同.pdf');
|
|
|
+ assert.equal(ctx.body.data.files[0].filepath, undefined);
|
|
|
+});
|
|
|
+
|
|
|
+test('合同文件下载拒绝其他合同的文件', async () => {
|
|
|
+ const spid = '5472d997-c4ae-4421-8265-feb6ab0833db';
|
|
|
+ const projectId = '06c6dd13-e52b-4c37-9d90-468cf32942aa';
|
|
|
+ let readOss = false;
|
|
|
+ const ctx = {
|
|
|
+ query: { spid, type: 'expenses', cid: 'contract-row-id' },
|
|
|
+ params: { fid: '2' },
|
|
|
+ session: {
|
|
|
+ sessionProject: { id: projectId },
|
|
|
+ sessionUser: { is_admin: true },
|
|
|
+ },
|
|
|
+ service: {
|
|
|
+ subProject: {
|
|
|
+ getDataById: async () => ({ id: spid, project_id: projectId, page_show: '{}' }),
|
|
|
+ getPageShow: () => ({ openContract: true }),
|
|
|
+ },
|
|
|
+ subProjPermission: { adminPermission: {} },
|
|
|
+ contract: {
|
|
|
+ getListByUsers: async () => [{ id: 'contract-row-id', name: '测试合同' }],
|
|
|
+ },
|
|
|
+ contractAtt: {
|
|
|
+ getDataById: async () => ({ id: 2, cid: 'another-contract', spid, contract_type: 1, filepath: 'private/file.pdf' }),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ helper: {
|
|
|
+ ossFileGet: async () => {
|
|
|
+ readOss = true;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const Controller = makeController({ BaseController: class {} });
|
|
|
+ await Controller.prototype.downloadFile.call({ log: () => {} }, ctx);
|
|
|
+
|
|
|
+ assert.equal(ctx.status, 404);
|
|
|
+ assert.equal(ctx.body.code, -1);
|
|
|
+ assert.equal(readOss, false);
|
|
|
+});
|
|
|
+
|
|
|
+test('标段合同文件通过所属标段校验后返回文件内容', async () => {
|
|
|
+ const spid = '5472d997-c4ae-4421-8265-feb6ab0833db';
|
|
|
+ const projectId = '06c6dd13-e52b-4c37-9d90-468cf32942aa';
|
|
|
+ const headers = {};
|
|
|
+ const ctx = {
|
|
|
+ query: { spid, tid: '4852', type: 'income', cid: 'contract-row-id' },
|
|
|
+ params: { fid: '2' },
|
|
|
+ session: {
|
|
|
+ sessionProject: { id: projectId },
|
|
|
+ sessionUser: { is_admin: true },
|
|
|
+ },
|
|
|
+ service: {
|
|
|
+ subProject: {
|
|
|
+ getDataById: async () => ({ id: spid, project_id: projectId, page_show: '{}' }),
|
|
|
+ getPageShow: () => ({ openTenderContract: true }),
|
|
|
+ },
|
|
|
+ subProjPermission: { adminPermission: {} },
|
|
|
+ tender: { getDataById: async () => ({ id: 4852, spid, project_id: projectId }) },
|
|
|
+ contract: {
|
|
|
+ getListByUsers: async options => {
|
|
|
+ assert.deepEqual(options, { tid: 4852, contract_type: 2 });
|
|
|
+ return [{ id: 'contract-row-id' }];
|
|
|
+ },
|
|
|
+ },
|
|
|
+ contractAtt: {
|
|
|
+ getDataById: async () => ({
|
|
|
+ id: 2, cid: 'contract-row-id', tid: 4852, contract_type: 2,
|
|
|
+ filename: '合同.pdf', fileext: '.pdf', filepath: 'private/file.pdf',
|
|
|
+ }),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ response: { set: values => Object.assign(headers, values) },
|
|
|
+ helper: {
|
|
|
+ ossFileGet: async path => {
|
|
|
+ assert.equal(path, 'private/file.pdf');
|
|
|
+ return Buffer.from('test file');
|
|
|
+ },
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const Controller = makeController({ BaseController: class {} });
|
|
|
+ await Controller.prototype.downloadFile.call({ log: () => {} }, ctx);
|
|
|
+
|
|
|
+ assert.equal(headers['Content-Type'], 'application/pdf');
|
|
|
+ assert.deepEqual(ctx.body, Buffer.from('test file'));
|
|
|
+});
|