unit_price_model.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /**
  2. * 单价业务模型
  3. *
  4. * @author CaiAoLin
  5. * @date 2017/6/30
  6. * @version
  7. */
  8. const mongoose = require("mongoose");
  9. const BaseModel = require("../../common/base/base_model");
  10. const GLJTypeConst = require("../../common/const/glj_type_const");
  11. const CounterModel = require("./counter_model");
  12. const MixRatioModel = require("./mix_ratio_model");
  13. const _ = require("lodash");
  14. const scMathUtil = require('../../../public/scMathUtil').getUtil();
  15. let collectionName = 'unit_price';
  16. let decimal_facade = require('../../main/facade/decimal_facade');
  17. let gljListModel = mongoose.model("glj_list");
  18. class UnitPriceModel extends BaseModel {
  19. /**
  20. * 构造函数
  21. *
  22. * @return {void}
  23. */
  24. constructor() {
  25. let parent = super();
  26. parent.model = mongoose.model(collectionName);
  27. parent.init();
  28. }
  29. /**
  30. * 根据单价文件id获取单价数据
  31. *
  32. * @param {Number} fileId
  33. * @return {Promise}
  34. */
  35. async getDataByFileId(fileId) {
  36. fileId = parseInt(fileId);
  37. if (isNaN(fileId) || fileId <= 0) {
  38. return null;
  39. }
  40. let unitPriceList = await this.db.model.find({unit_price_file_id: fileId});
  41. if (unitPriceList.length <= 0) {
  42. return null;
  43. }
  44. // 整理数据
  45. let result = {};
  46. for(let tmp of unitPriceList) {
  47. let index = this.getIndex(tmp,['code','name','specs','unit','type'])
  48. result[index] = tmp;
  49. }
  50. return result;
  51. }
  52. /**
  53. * 设置场景
  54. *
  55. * @param {string} scene
  56. * @return {void}
  57. */
  58. setScene(scene = '') {
  59. switch (scene) {
  60. // 新增数据的验证规则
  61. case 'add':
  62. this.model.schema.path('base_price').required(true);
  63. this.model.schema.path('market_price').required(true);
  64. this.model.schema.path('name').required(true);
  65. this.model.schema.path('code').required(true);
  66. // this.model.schema.path('unit').required(true);
  67. this.model.schema.path('type').required(true);
  68. this.model.schema.path('unit_price_file_id').required(true);
  69. }
  70. }
  71. /**
  72. * 新增单价数据
  73. *
  74. * @param {Object} data
  75. * @param {Number} unitPriceFileId
  76. * @param {Number} gljCount
  77. * @return {Promise} 返回数据以及是否新增
  78. */
  79. async addUnitPrice(data, unitPriceFileId,operation='add', gljCount = 0) {
  80. if (data.original_code===undefined||data.code === undefined || data.project_id === undefined || data.name === undefined
  81. || data.market_price === undefined) {
  82. return [null, false];
  83. }
  84. // 先查找是否有原始code相同的记录
  85. let unitPriceData = await this.db.model.find({original_code: data.original_code, unit_price_file_id: unitPriceFileId}).sort('code').exec();
  86. // 如果有记录,判断是否存在一样的名称,单位...等,有则直接返回数据
  87. let unitPrice=null;
  88. if(operation=='add'){//新增操作时,要把code也一起判断,是否完全一样。(新增的时候有可能存在编码一样,但是名称规格等不一样的情况,这种情况的话编码不用改变)
  89. unitPrice = this.isPropertyInclude(unitPriceData,['code','name','specs','unit','type'],data);
  90. }else {//修改操作时,code不用加入判断,因为code是需要改变的
  91. unitPrice = this.isPropertyInclude(unitPriceData,['name','specs','unit','type'],data);
  92. }
  93. if(unitPrice){
  94. return [unitPrice, false];
  95. }
  96. // 如果不存在基价单价,则在数据源中获取
  97. if (data.base_price === undefined) {
  98. let firstUnitPrice = unitPriceData[0] !== undefined ? unitPriceData[0] : [];
  99. data.base_price = firstUnitPrice.base_price !== undefined ? firstUnitPrice.base_price : 0;
  100. data.type = firstUnitPrice.type !== undefined ? firstUnitPrice.type : 0;
  101. }
  102. let insertData = {
  103. code: data.code,
  104. base_price: data.base_price,
  105. market_price: data.market_price,
  106. taxRate:data.taxRate,
  107. unit_price_file_id: unitPriceFileId,
  108. name: data.name,
  109. specs:data.specs,
  110. original_code:data.original_code,
  111. unit:data.unit,
  112. type: data.type,
  113. short_name: data.shortName !== undefined ? data.shortName : '',
  114. glj_id: data.glj_id,
  115. is_add:0
  116. };
  117. if(data.from=='cpt') insertData.is_add=1; //如果是来自补充工料机,则都添加新增标记
  118. if(operation=='add' && insertData.code != insertData.original_code) insertData.is_add=1;//添加的时候如果是复制整块来的,可能在源项目中是新增的工料机,这里也要添上
  119. if (unitPriceData&&unitPriceData.length>0&&operation!='add') {// 如果原始编码能找到,但不存在一样的编号,名称,单位.型号等,更改code和添加新增标记,新增的时候除外。新增的情况下能到这一步说明有存在编码一致但其它属性不一致的情况,所以不用更改编码
  120. //insertData.code = data.original_code+"-"+unitPriceData.length;
  121. insertData.code = data.original_code+"-"+this.getLastNumber(data.original_code,unitPriceData);
  122. insertData.is_add=1;
  123. }
  124. let addPriceResult = await this.add(insertData);
  125. return [addPriceResult, true];
  126. }
  127. getLastNumber(original_code,unitPriceData){
  128. let codeArray = _.map(unitPriceData,'code');
  129. let last = 1;
  130. while (true){
  131. if(_.includes(codeArray,original_code+"-"+last)){
  132. last +=1
  133. }else {
  134. break;
  135. }
  136. }
  137. return last;
  138. }
  139. /**
  140. * 新增记录
  141. *
  142. * @param {object} data
  143. * @return {Promise}
  144. */
  145. async add(data) {
  146. let counterModel = new CounterModel();
  147. if (data instanceof Array) {
  148. // 如果是批量新增
  149. await this.setIDfromCounter(collectionName,data);
  150. /* for(let tmp in data) {
  151. data[tmp].id = await counterModel.getId(collectionName);
  152. } */
  153. } else {
  154. data.id = await counterModel.getId(collectionName);
  155. }
  156. this.setScene('add');
  157. return this.db.model.create(data);
  158. }
  159. /**
  160. * 判断数据中是否包含某个市场价格的记录
  161. *
  162. * @param {Array} data
  163. * @param {Number} price
  164. * @return {Number}
  165. */
  166. isPriceIncluded(data, price) {
  167. let index = -1;
  168. if (data.length <= 0) {
  169. return index;
  170. }
  171. for(let tmp in data) {
  172. if (data[tmp].market_price === price) {
  173. index = tmp;
  174. break;
  175. }
  176. }
  177. return index;
  178. }
  179. isPropertyInclude(data,pops,obj){
  180. let condition={},me = this;
  181. if (data.length <= 0) {
  182. return null;
  183. }
  184. if(pops instanceof Array){
  185. return _.find(data,function (d) {
  186. return me.getIndex(d,pops) == me.getIndex(obj,pops)
  187. });
  188. }else {
  189. condition[pops]=obj[pops];
  190. return _.find(data,condition);
  191. }
  192. }
  193. /**
  194. * 更新市场单价
  195. *
  196. * @param {Object} condition
  197. * @param {Object} updateData
  198. * @param {String} extend
  199. * @return {Promise}
  200. */
  201. async updatePrice(condition, updateData, extend = '') {
  202. if (Object.keys(condition).length <= 0 || Object.keys(updateData).length <= 0) {
  203. return false;
  204. }
  205. // 首先查找相应的数据判断工料机类型
  206. let unitPriceData = await this.findDataByCondition(condition);
  207. if (!unitPriceData) {
  208. throw '找不到对应的单价数据';
  209. }
  210. /* // 基价单价的计算-----先不考虑同步
  211. switch (unitPriceData.type) {
  212. // 主材、设备自动赋值基价单价=市场单价
  213. case GLJTypeConst.MAIN_MATERIAL:
  214. case GLJTypeConst.EQUIPMENT:
  215. updateData.base_price = updateData.market_price;
  216. break;
  217. }*/
  218. // 额外更新数据
  219. if (extend !== '') {
  220. extend = JSON.parse(extend);
  221. let indexList = ['code','name','specs','unit','type'];
  222. for (let conKey in extend) {
  223. let extendUpdateData = {
  224. market_price: extend[conKey].market_price,
  225. };
  226. let tmpCondition = {
  227. unit_price_file_id: unitPriceData.unit_price_file_id,
  228. };
  229. let keyList = conKey.split("|-|");
  230. for(let i = 1;i<keyList.length;i++){
  231. if(keyList[i]!='null'){
  232. tmpCondition[indexList[i]]=keyList[i];
  233. }
  234. }
  235. let extendResult = await this.db.update(tmpCondition, extendUpdateData);
  236. if (!extendResult) {
  237. throw '更新额外数据,编码为' + code + '的数据失败!';
  238. }
  239. }
  240. }
  241. let result = await this.db.update(condition, updateData);
  242. return result.ok !== undefined && result.ok === 1;
  243. }
  244. async updateUnitPrice(data){
  245. //查找并更新单价
  246. let newValueMap={};
  247. let doc = data.udoc?data.udoc:{};
  248. doc[data.field]=data.newval;
  249. newValueMap[data.id]=doc;
  250. let unitPrice = await this.db.findAndModify({id:data.id,unit_price_file_id:data.unit_price_file_id},doc);
  251. if(!unitPrice){
  252. throw "没有找到对应的单价";
  253. }
  254. let rList= this.checkAndUpdateParent(unitPrice,data.field,data.project_id,newValueMap);
  255. return rList;
  256. }
  257. async checkAndUpdateParent(unitPrice,field,project_id,newValueMap,batchUpdate=false){//检查是否属于某个工料机的组成物,如果是,并且不是批量更新的情况下,直接更新,如果是批量更新,返回更新任务
  258. //查找是否是属于某个项目工料机的组成物
  259. let mixRatioModel = new MixRatioModel();
  260. let condition = {unit_price_file_id:unitPrice.unit_price_file_id, code:unitPrice.code,name: unitPrice.name, specs: unitPrice.specs,unit:unitPrice.unit,type:unitPrice.type};
  261. let mixRatioList = await mixRatioModel.findDataByCondition(condition, null, false);
  262. let connectKeyMap={};
  263. //找到则计算项目工料机组成物的价格并更新
  264. let rList= [];
  265. if(mixRatioList&&mixRatioList.length>0){
  266. for(let m of mixRatioList){
  267. if(!connectKeyMap.hasOwnProperty(m.connect_key)){//为了去重复,组成物会与其它项目同步,所以有可能重复。
  268. rList.push(await this.updateParentUnitPrice(m,field,project_id,newValueMap,batchUpdate));
  269. connectKeyMap[m.connect_key]=true;
  270. }
  271. }
  272. }
  273. return rList;
  274. }
  275. async batchUpdatePrices(data){//批量更新
  276. let tasks = [];
  277. let parentTask = [];
  278. let newValueMap = {};
  279. let needCheckDatas= [];
  280. for(let d of data){//第一次循环生成更新提交的记录,并生成一个新值的映射表,为更新父节点使用
  281. let condition = {id:d.unit_price.id,unit_price_file_id:d.unit_price.unit_price_file_id};
  282. let doc = d.ext?d.ext:{};
  283. if(d.field){//共用接口后有可能只更新其它属性,不更新价格
  284. doc[d.field]=d.newval;
  285. newValueMap[d.unit_price.id] = doc;
  286. needCheckDatas.push(d);
  287. }
  288. tasks.push(this.generateUpdateTask(condition,doc));
  289. }
  290. for(let d of needCheckDatas){//第二次更新父节点
  291. let rList = await this.checkAndUpdateParent(d.unit_price,d.field,d.project_id,newValueMap,true);
  292. parentTask = parentTask.concat(rList);
  293. }
  294. tasks = tasks.concat(parentTask);
  295. tasks.length>0?this.model.bulkWrite(tasks):'';
  296. return parentTask;
  297. }
  298. async updateParentUnitPrice(mixRatio,fieid,project_id,newValueMap,batchUpdate){//batchUpdate 批量更新标记,如果true,只生成task
  299. let decimalObject =project_id?await decimal_facade.getProjectDecimal(project_id):null
  300. let quantity_decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.quantity)?decimalObject.glj.quantity:3;
  301. let price_decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.unitPrice)?decimalObject.glj.unitPrice:2;
  302. //查找该工料机所有组成物
  303. let indexList = ['code','name','specs','unit','type'];
  304. let mixRatioModel = new MixRatioModel();
  305. let mixRatioMap = await mixRatioModel.findDataByCondition({unit_price_file_id:mixRatio.unit_price_file_id,connect_key:mixRatio.connect_key}, null, false,indexList);
  306. //查找对应的价格
  307. let codeList = [];
  308. let nameList = [];
  309. let specsList= [];
  310. let typeList = [];
  311. let unitList = [];
  312. for(let mk in mixRatioMap){
  313. codeList.push(mixRatioMap[mk].code);
  314. nameList.push(mixRatioMap[mk].name);
  315. specsList.push(mixRatioMap[mk].specs);
  316. typeList.push(mixRatioMap[mk].type);
  317. unitList.push(mixRatioMap[mk].unit);
  318. }
  319. let condition = {unit_price_file_id: mixRatio.unit_price_file_id,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList}};
  320. let priceMap = await this.findDataByCondition(condition, {_id: 0}, false, indexList);
  321. let sumPrice=0;
  322. for(let pk in priceMap){
  323. let price = scMathUtil.roundForObj(priceMap[pk][fieid],price_decimal);
  324. let consumption = scMathUtil.roundForObj(mixRatioMap[pk].consumption,quantity_decimal);
  325. if(newValueMap[priceMap[pk].id]){//是需要更新的记录,取当前新的值
  326. price = scMathUtil.roundForObj(newValueMap[priceMap[pk].id][fieid],price_decimal);
  327. }
  328. sumPrice +=scMathUtil.roundForObj(price*consumption,price_decimal);
  329. }
  330. sumPrice= scMathUtil.roundForObj(sumPrice,price_decimal);
  331. if(sumPrice<=0){
  332. return null;
  333. }
  334. //更新父价格
  335. let keyList = mixRatio.connect_key.split("|-|");
  336. let pcondition = {
  337. unit_price_file_id:mixRatio.unit_price_file_id,
  338. code:keyList[0]
  339. };
  340. for(let i = 1;i<keyList.length;i++){
  341. if(keyList[i]!='null'){
  342. pcondition[indexList[i]]=keyList[i];
  343. }
  344. }
  345. let doc={};
  346. doc[fieid]=sumPrice;
  347. if(batchUpdate == true){
  348. return this.generateUpdateTask(pcondition,doc);
  349. }else {
  350. let uprice = await this.db.findAndModify(pcondition,doc,{new: true});
  351. //uprice[fieid]=sumPrice;
  352. return uprice;
  353. }
  354. }
  355. generateUpdateTask(condition,doc) {
  356. let task = {
  357. updateOne:{
  358. filter:condition,
  359. update:doc
  360. }
  361. };
  362. return task
  363. }
  364. /**
  365. * 复制单价文件数据
  366. *
  367. * @param {Number} currentUnitPriceId
  368. * @param {Number} changeUnitPriceId
  369. * @return {Promise}
  370. */
  371. async copyNotExist(currentUnitPriceId, changeUnitPriceId,projectId) {
  372. let result = false;
  373. // 首先查找原单价文件id下的数据
  374. let currentUnitList = await this.model.find({unit_price_file_id: currentUnitPriceId}).lean();
  375. if (currentUnitList === null) {
  376. return result;
  377. }
  378. let gljList = await gljListModel.find({'project_id':projectId});
  379. let gljMap = {};//用来记录glj的映射表,本项目有使用的工料机才需要copy过去
  380. for(let g of gljList){
  381. let g_index = this.getIndex(g,['code','name','specs','unit','type']);
  382. gljMap[g_index] = g;
  383. }
  384. let codeList = [];
  385. let nameList =[];
  386. for (let tmp of currentUnitList) {
  387. if (codeList.indexOf(tmp.code) >= 0) {
  388. continue;
  389. }
  390. codeList.push(tmp.code);
  391. if(nameList.indexOf(tmp.name)>=0){
  392. continue
  393. }
  394. nameList.push(tmp.name);
  395. }
  396. // 查找即将更替的单价文件是否存在对应的工料机数据 -- (这里只根据code和名称初步过滤,因为其它的几项更改的概率不大,在下一步的比较中再精确匹配)
  397. let condition = {unit_price_file_id: changeUnitPriceId, code: {"$in": codeList},name:{"$in": nameList}};
  398. let targetUnitList = await this.findDataByCondition(condition, null, false, ['code','name','specs','unit','type']);
  399. // 如果没有重叠的数据则原有的数据都复制一份
  400. let insertData = [];
  401. for (let tmp of currentUnitList) {
  402. let t_index = this.getIndex(tmp,['code','name','specs','unit','type']);
  403. if (targetUnitList !== null && targetUnitList[t_index] !== undefined) {
  404. continue;
  405. }
  406. if(gljMap[t_index]){//如果本项目有用到才复制
  407. delete tmp._id; // 删除原有id信息
  408. delete tmp.id;
  409. tmp.unit_price_file_id = changeUnitPriceId;
  410. insertData.push(tmp);
  411. }
  412. }
  413. let uResult = insertData.length > 0 ? this.add(insertData) : true;
  414. let mixRatioModel = new MixRatioModel();
  415. let mResult = await mixRatioModel.copyNotExist(currentUnitPriceId, changeUnitPriceId,gljMap);//复制组成物
  416. return uResult&&mResult;
  417. }
  418. }
  419. module.exports = UnitPriceModel;