/** * 项目工料机列表数据模型 * * @author CaiAoLin * @date 2017/6/22 * @version */ import BaseModel from "../../common/base/base_model"; import {default as GLJSchemas, collectionName as gljCollectionName} from "./schemas/glj"; import CounterModel from "./counter_model"; import UnitPriceModel from "./unit_price_model"; import UnitPriceFileModel from "./unit_price_file_model"; import GLJTypeConst from "../../common/const/glj_type_const"; import RationGLJFacade from "../../ration_glj/facade/ration_glj_facade"; import STDGLJLibGLJListModel from "../../common/std/std_glj_lib_glj_list_model"; import MixRatioModel from "./mix_ratio_model"; import GljModel from "../../complementary_glj_lib/models/gljModel"; const ProjectModel = require('../../pm/models/project_model').project; const scMathUtil = require('../../../public/scMathUtil').getUtil(); class GLJListModel extends BaseModel { /** * 材料、主材、设备类型id * * @var {Array} */ materialIdList = [GLJTypeConst.GENERAL_MATERIAL, GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO, GLJTypeConst.COMMERCIAL_CONCRETE, GLJTypeConst.COMMERCIAL_MORTAR, GLJTypeConst.MAIN_MATERIAL, GLJTypeConst.EQUIPMENT]; /** * 拥有组成物的工料机类型id * * @var {Array} */ ownCompositionTypes = [GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO, GLJTypeConst.COMMERCIAL_CONCRETE, GLJTypeConst.COMMERCIAL_MORTAR, GLJTypeConst.GENERAL_MACHINE]; /** * 构造函数 * * @return {void} */ constructor() { let parent = super(); parent.model = GLJSchemas; parent.init(); } /** * 设置场景 * * @param {string} scene * @return {void} */ setScene(scene = '') { switch (scene) { // 新增数据的验证规则 case 'add': this.model.schema.path('glj_id').required(true); this.model.schema.path('project_id').required(true); this.model.schema.path('code').required(true); // this.model.schema.path('name').required(true); break; } } /** * 根据标段对应项目工料机列表 * * @param {Number} projectId * @param {Number} unitPriceFileId * @return {Promise} */ async getListByProjectId(projectId, unitPriceFileId) { let gljData = null; let mixRatioConnectData = {}; try { // 首先获取对应标段下所有的项目工料机数据 let condition = {project_id: projectId}; let fields = {_id: 0}; gljData = await this.db.find(condition, fields); // 没有数据则直接返回空 if (gljData.length <= 0) { throw '无数据'; } // 获取标段设置的单价文件数据 let unitPriceModel = new UnitPriceModel(); let unitPriceList = await unitPriceModel.getDataByFileId(unitPriceFileId); // 整理获取工料机ID list let gljIdList = []; for(let tmp of gljData) { gljIdList.push(tmp.id); } // 从定额工料机库中获取消耗量 condition = { projectID: projectId, projectGLJIDList: gljIdList }; let quantityData = await RationGLJFacade.getQuantityByProjectGLJ(condition); let quantityList = {}; // 整理数据 for (let tmp of quantityData) { let tmpNum = parseFloat(tmp.rationQuantity); tmpNum = isNaN(tmpNum) ? 1 : tmpNum; if (quantityList[tmp.projectGLJID] === undefined) { quantityList[tmp.projectGLJID] = tmp.quantity * tmpNum; } else { quantityList[tmp.projectGLJID] += tmp.quantity * tmpNum; } } // 整理获取有组成物的项目工料机编码 let projectGLJCode = []; for(let tmp of gljData) { // 有组成物的类型且消耗量大于0才查找 if (quantityList[tmp.id] !== undefined) { projectGLJCode.push(tmp.code); } } // 查找组成物的消耗量 let totalComposition = {}; let mixRatioData = {}; if (projectGLJCode.length > 0) { let mixRatioModel = new MixRatioModel(); condition = {connect_code: {"$in": projectGLJCode}, unit_price_file_id: unitPriceFileId}; let mixRatioList = await mixRatioModel.findDataByCondition(condition, null, false); for (let tmp of mixRatioList) { totalComposition[tmp.connect_code] = totalComposition[tmp.connect_code] === undefined ? tmp.consumption : totalComposition[tmp.connect_code] + tmp.consumption; totalComposition[tmp.connect_code] = scMathUtil.roundTo(totalComposition[tmp.connect_code], -4); if (mixRatioData[tmp.glj_id] !== undefined) { mixRatioData[tmp.glj_id].push(tmp); } else { mixRatioData[tmp.glj_id] = [tmp]; } if (mixRatioConnectData[tmp.glj_id] !== undefined) { mixRatioConnectData[tmp.glj_id].push(tmp.connect_code); } else { mixRatioConnectData[tmp.glj_id] = [tmp.connect_code]; } } } // 组合单价数据 gljData = this.combineData(gljData, unitPriceList, quantityList, mixRatioData, totalComposition); // 排序 gljData.sort(function (a, b) { a.unit_price = a.unit_price === null ? 0 : a.unit_price; b.unit_price = b.unit_price === null ? 0 : b.unit_price; return a.unit_price.type - b.unit_price.type; }); } catch (error) { console.log("glj_list_model:" + error); gljData = []; } return [gljData, mixRatioConnectData]; } /** * 组合工料机数据和单价文件数据 * * @param {object} gljList * @param {object} unitPriceList * @param {object} quantityList * @param {object} mixRatioData 组合物明细数据 * @param {object} totalComposition 组合物父工料机统计数据 * @return {Array} */ combineData(gljList, unitPriceList, quantityList = {}, mixRatioData = {}, totalComposition = {}) { // 整理组成物消耗量(只有在总列表显示的时候才需用到,获取单项项目工料机内容则忽略) let compositionConsumption = {}; if (Object.keys(mixRatioData).length > 0 && Object.keys(totalComposition).length > 0) { for(let index in mixRatioData) { for(let tmp of mixRatioData[index]) { compositionConsumption[tmp.glj_id] = compositionConsumption[tmp.glj_id] === undefined ? tmp.consumption : compositionConsumption[tmp.glj_id] + tmp.consumption; } } } let result = []; // 循环组合数据 for(let index in gljList) { let glj = gljList[index]; if (glj.code === undefined) { continue; } glj.unit_price = unitPriceList !== null && unitPriceList[glj.code + glj.name] !== undefined ? unitPriceList[glj.code + glj.name] : null; if (glj.unit_price === null) { continue; } let gljId = glj.glj_id + ''; let projectGljId = glj.id + ''; // 消耗量赋值 glj.quantity = quantityList[projectGljId] !== undefined ? quantityList[projectGljId] : 0; glj.quantity = totalComposition[glj.code] !== undefined ? totalComposition[glj.code] : glj.quantity; glj.quantity = compositionConsumption[gljId] !== undefined ? glj.quantity + compositionConsumption[gljId] : glj.quantity; glj.quantity = scMathUtil.roundTo(parseFloat(glj.quantity), -3); // 组成物数据 gljList[index].ratio_data = mixRatioData[gljId] !== undefined ? mixRatioData[gljId] : []; glj.unit_price.base_price = scMathUtil.roundTo(parseFloat(glj.unit_price.base_price), -2); glj.unit_price.market_price = scMathUtil.roundTo(parseFloat(glj.unit_price.market_price), -2); // 计算调整基价 switch (glj.unit_price.type + '') { // 人工: 调整基价=基价单价*调整系数 case GLJTypeConst.LABOUR: glj.adjust_price = scMathUtil.roundTo(parseFloat(glj.adjustment * glj.unit_price.base_price), -2); break; // 机械类型的算法 case GLJTypeConst.MACHINE: console.log('机械'); break; // 材料、主材、设备 default: glj.adjust_price = glj.unit_price.base_price; } result.push(glj); } return result; } /** * 新增项目工料机数据(包括新增单价文件) 定额工料机新增时调用 * * @param {object} data * @return {Promise} 返回插入成功的数据id */ async addList(data) { let result = null; try { if (Object.keys(data).length <= 0) { throw '新增数据为空'; } // 首先查找是否有同编码同名称的工料机数据 let projectGljData = await this.findDataByCondition({code: data.code, project_id: data.project_id}); let isAddProjectGLJ = false; // 如果找不到数据则新增 if (!projectGljData) { // 新增单条记录 (两个操作本来应该是事务操作,然而mongodb事务支持比较弱,就当作是都可以顺利执行) let gljInsertData = await this.add(data); if (!gljInsertData) { throw '新增项目工料机失败!'; } isAddProjectGLJ = true; projectGljData = gljInsertData; } // 获取标段对应的单价文件id let unitPriceFileId = await ProjectModel.getUnitPriceFileId(data.project_id); if (unitPriceFileId <= 0) { throw '没有对应的单价文件'; } // 判断类型,如果是混凝土、砂浆或者配合比则查找对应的组成物(前提是没有对应的项目工料机数据) if (isAddProjectGLJ && (data.type === GLJTypeConst.CONCRETE || data.type === GLJTypeConst.MORTAR || data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE)) { this.compositionInit(data, unitPriceFileId); } // 新增单价文件 let unitPriceModel = new UnitPriceModel(); let [unitPriceInsertData, isAdd] = await unitPriceModel.addUnitPrice(data, unitPriceFileId); if (!unitPriceInsertData) { throw '新增单价失败!'; } projectGljData.unit_price = unitPriceInsertData; result = projectGljData; } catch (error) { console.log(error); result = null; } return result; } /** * 新增单条工料机数据 * * @param {object} data * @return {Promise} */ async add(data) { if (Object.keys(data).length <= 0) { throw '新增数据为空'; } let counterModel = new CounterModel(); if (data instanceof Array) { // 如果是批量新增 for(let tmp in data) { data[tmp].id = await counterModel.getId(gljCollectionName); } } else { data.id = await counterModel.getId(gljCollectionName); } this.setScene('add'); let result = await this.db.create(data); return result; } /** * 根据工料机id修改市场单价 * * @param {Object} updateData * @return {Promise} */ async modifyMarketPrice(updateData) { let result = {}; try { if (updateData.code === undefined || updateData.market_price === undefined || updateData.name === undefined || updateData.project_id === undefined) { throw '参数有误!'; } // 先查是否有对应code的数据 let gljListData = await this.findDataByCondition({code: updateData.code, project_id: updateData.project_id}, {_id: 0}, false); if (!gljListData) { throw '不存在对应code数据'; } // 获取标段对应的单价文件id let unitPriceFileModel = new UnitPriceFileModel(); let unitPriceFile = await unitPriceFileModel.getDataByProject(updateData.project_id); if (!unitPriceFile) { throw '没有对应的单价文件'; } let unitPriceFileId = unitPriceFile.id; let unitPriceModel = new UnitPriceModel(); let gljCount = gljListData.length; let [unitPriceData, isAdd] = await unitPriceModel.addUnitPrice(updateData, unitPriceFileId, gljCount); // 判断是否已存在对应数据 let includeField = [ {field: 'name', value: unitPriceData.name} ]; let gljIndex = this.isIncluded(gljListData, includeField); let gljData = isAdd ? {} : gljListData[gljIndex]; // 如果单价数据新增则工料机也需要新增 if (isAdd) { // 如果没有对应的记录则新增一条工料机数据,并更改name let regular = /\(\d+\)/; let changeString = '(' + gljCount + ')'; updateData.name = regular.test(updateData.name) ? updateData.name.replace(regular, changeString) : updateData.name + changeString; // 获取第一条数据作为数据源 let originalData = gljListData[0]; // 更改名称 originalData.name = updateData.name; originalData = JSON.stringify(originalData); gljData = await this.add(JSON.parse(originalData)); if (!gljData) { throw '新增工料机数据失败!'; } } gljData.unit_price = unitPriceData; result = gljData; } catch (error) { console.log(error); result = {}; } return result; } /** * 判断数据中是否包含某个数据 * * @param {Array} data * @param {Array} includeField * @return {Number} */ isIncluded(data, includeField) { let index = -1; if (data.length <= 0) { return index; } for(let tmp in data) { let counter = 0; for (let includeTmp of includeField) { if (data[tmp][includeTmp.field] === includeTmp.value) { counter++; } } if (counter === includeField.length) { index = tmp; break; } } return index; } /** * 工料机中组成物操作 * 该方法只在确保没有对应项目工料机的时候才会调用 * * @param {Object} data * @param {Number} projectId * @return {void} */ async compositionInit(data, unitPriceFileId) { let gljId = data.glj_id === undefined ? 0 : data.glj_id; let projectId = data.project_id === undefined ? 0 : data.project_id; if (gljId === 0 || projectId === 0) { throw '参数错误'; } let fromTable = data.from === undefined ? 'std' : data.from; // 查找对应组成物的项目工料机数据 let [projectGljList, compositionGljList] = await this.getCompositionGLJList(gljId, projectId, 'name', fromTable); // 整理配合比待插入数据 let mixRatioInsertData = []; for (let tmp of compositionGljList) { // 配合比数据插入 let mixRatioData = { consumption: tmp.consumption, glj_id: tmp.ID, unit_price_file_id: unitPriceFileId, connect_code: tmp.connectCode, glj_type: tmp.gljType, code: tmp.code, }; mixRatioInsertData.push(mixRatioData); } // 插入配合比表 // 因为有可能项目工料机与单价数据已存在,但配合比数据不存在,所以先插入配合比,后续判断如果存在项目工料机则可以省下数据库操作 let mixRatioModel = new MixRatioModel(); let addMixRatioResult = await mixRatioModel.add(mixRatioInsertData); if (!addMixRatioResult) { throw '组成物插入单价数据失败!'; } // 如果已经存在则后续操作停止 if(projectGljList.length === compositionGljList.length) { return; } // 整理插入的数据 let gljInsertData = []; let unitPriceInsertData = []; for(let tmp of compositionGljList) { if (projectGljList[tmp.name] !== undefined) { continue; } // 项目工料机插入的数据 let gljData = { glj_id: tmp.ID, project_id: projectId, code: tmp.code, name: tmp.name, specs: tmp.specs, unit: tmp.unit === undefined ? '' : tmp.unit, }; gljInsertData.push(gljData); // 单价文件插入的数据 let unitPriceData = { base_price: tmp.basePrice, // 初始市场价=基价 market_price: tmp.basePrice, code: tmp.code, name: tmp.name, unit_price_file_id: unitPriceFileId, type: tmp.gljType, short_name: tmp.shortName === undefined ? '' : tmp.shortName, glj_id: tmp.ID, }; unitPriceInsertData.push(unitPriceData); } // 整理完后开始插入数据 let addResult = await this.add(gljInsertData); if (!addResult) { throw '组成物插入项目工料机失败!'; } // 插入单价数据表 let unitPriceModel = new UnitPriceModel(); let addUnitPriceResult = await unitPriceModel.add(unitPriceInsertData); if (!addUnitPriceResult) { throw '组成物插入单价数据失败!'; } } /** * 获取组成物具体数据 * * @param {Number} projectGLJId * @param {Number} unitPriceFileId * @return {Promise} */ async getCompositionList(projectGLJId, unitPriceFileId) { let result = []; try { // 查找对应的项目工料机数据 let projectGLJData = await this.getDataById(projectGLJId); let allowType = [GLJTypeConst.MIX_RATIO, GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.GENERAL_MACHINE]; if (projectGLJData.unit_price === null || allowType.indexOf(projectGLJData.unit_price.type) < 0) { throw '找不到相关项目工料机'; } // 查找对应的项目工料机数据 let [gljData, compositionList] = await this.getCompositionGLJList(projectGLJData.glj_id, projectGLJData.project_id); if (gljData.length <= 0) { throw '没有对应的组成物项目工料机'; } // 整理出code和name查找相关单价数据 let codeList = []; let nameList = []; let gljIdList = []; for(let tmp of gljData) { codeList.push(tmp.code); nameList.push(tmp.name); gljIdList.push(tmp.glj_id); } // 查找对应的单价数据 let unitPriceModel = new UnitPriceModel(); let condition = {code: {"$in": codeList}, name: {"$in": nameList}, unit_price_file_id: unitPriceFileId}; let unitPriceList = await unitPriceModel.findDataByCondition(condition, {_id: 0}, false); // 查找对应的配合比数据 let mixRatioModel = new MixRatioModel(); condition = {glj_id: {"$in": gljIdList}, connect_code: projectGLJData.code, unit_price_file_id: unitPriceFileId}; let mixRatioData = await mixRatioModel.findDataByCondition(condition, {_id: 0}, false, 'glj_id'); // 整理数据 let unitPriceData = {}; for(let tmp of unitPriceList) { unitPriceData[tmp.code + tmp.name] = tmp; } gljData = this.combineData(gljData, unitPriceData, [], mixRatioData); // 排序 gljData.sort(function (a, b) { return parseInt(a.code) - parseInt(b.code); }); result = gljData; } catch (error) { console.log(error); result = []; } return result; } /** * 获取混凝土等有组成物相关工料机对应的组成物项目工料机数据 * * @param {Number} gljId * @param {Number} projectId * @param {String} indexBy * @param {String} fromTable * @return {Promise} 返回组成物工料机数据和组成物列表数据 */ async getCompositionGLJList(gljId, projectId, indexBy = null, fromTable = 'std') { // 获取对应的组成物数据 let gljListModel = fromTable === 'std' ? new STDGLJLibGLJListModel() : new GljModel(); let componentGljList = await gljListModel.getComponent(gljId); if (componentGljList.length <= 0) { throw '不存在对应的组成物'; } let codeList = []; let nameList = []; for(let tmp of componentGljList) { codeList.push(tmp.code); nameList.push(tmp.name); } // 查找对应的项目工料机数据 let condition = {code: {"$in": codeList}, name: {"$in": nameList}, project_id: projectId}; let gljData = await this.findDataByCondition(condition, {_id: 0}, false, indexBy); return [gljData, componentGljList]; } /** * 根据条件获取对应项目工料机数据 * * @param {Number} id * @return {Promise} */ async getDataById(id) { // 查找对应的项目工料机数据 let projectGLJData = await this.findDataByCondition({id: id}); if (projectGLJData === null) { throw '没有找到对应数据'; } // 查找对应的单价数据 let unitPriceModel = new UnitPriceModel(); let unitPrice = await unitPriceModel.findDataByCondition({code: projectGLJData.code, name: projectGLJData.name}); projectGLJData.unit_price = unitPrice; return projectGLJData; } } export default GLJListModel;