flexbiz-server 12.5.25 → 12.5.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/package.json +1 -1
  2. package/server/controllers/controller.js +3 -3
  3. package/server/controllers/controllerRPT.js +4 -4
  4. package/server/controllers/createRouteHandler.js +1 -1
  5. package/server/controllers/rptCreateRouteHandler.js +1 -1
  6. package/server/libs/joinData.js +6 -5
  7. package/server/models/mailaccount.js +2 -2
  8. package/server/models/productcode.js +2 -0
  9. package/server/modules/lists/ls-productcode.js +7 -0
  10. package/server/templates/add-file.html +8 -0
  11. package/server/templates/add-file.pug +9 -0
  12. package/server/templates/alert task.html +11 -0
  13. package/server/templates/alert task.pug +12 -0
  14. package/server/templates/alert warranty.html +15 -0
  15. package/server/templates/alert warranty.pug +28 -0
  16. package/server/templates/excels/PN9.xlsx +0 -0
  17. package/server/templates/excels/hd1.xlsx +0 -0
  18. package/server/templates/excels/hd2.xlsx +0 -0
  19. package/server/templates/excels/hd3.xlsx +0 -0
  20. package/server/templates/excels/lists/account.xlsx +0 -0
  21. package/server/templates/excels/lists/customer.xlsx +0 -0
  22. package/server/templates/excels/lists/dmvt.xlsx +0 -0
  23. package/server/templates/excels/pc1.xlsx +0 -0
  24. package/server/templates/excels/pkc.xlsx +0 -0
  25. package/server/templates/excels/pkh.xlsx +0 -0
  26. package/server/templates/excels/pkt.xlsx +0 -0
  27. package/server/templates/excels/pn1.xlsx +0 -0
  28. package/server/templates/excels/pn2.xlsx +0 -0
  29. package/server/templates/excels/pn3.xlsx +0 -0
  30. package/server/templates/excels/pn5.xlsx +0 -0
  31. package/server/templates/excels/pnk.xlsx +0 -0
  32. package/server/templates/excels/pt1.xlsx +0 -0
  33. package/server/templates/excels/pxc.xlsx +0 -0
  34. package/server/templates/excels/pxk.xlsx +0 -0
  35. package/server/templates/excels/reports/bangtinhkhauhao.xlsx +0 -0
  36. package/server/templates/excels/reports/bcdkt.xlsx +0 -0
  37. package/server/templates/excels/reports/bkct.xlsx +0 -0
  38. package/server/templates/excels/reports/bkvatra.xlsx +0 -0
  39. package/server/templates/excels/reports/bkvatvao.xlsx +0 -0
  40. package/server/templates/excels/reports/cdpsdt.xlsx +0 -0
  41. package/server/templates/excels/reports/cdpskh.xlsx +0 -0
  42. package/server/templates/excels/reports/cdpstk.xlsx +0 -0
  43. package/server/templates/excels/reports/chitietchitientheohoadon.xlsx +0 -0
  44. package/server/templates/excels/reports/chitiettaisan.xlsx +0 -0
  45. package/server/templates/excels/reports/chitietthutientheohoadon.xlsx +0 -0
  46. package/server/templates/excels/reports/ctbanle.xlsx +0 -0
  47. package/server/templates/excels/reports/cthangbanbitralai.xlsx +0 -0
  48. package/server/templates/excels/reports/ctmuahang.xlsx +0 -0
  49. package/server/templates/excels/reports/dtbanletheokh.xlsx +0 -0
  50. package/server/templates/excels/reports/dtbanletheonam.xlsx +0 -0
  51. package/server/templates/excels/reports/dtbanletheongay.xlsx +0 -0
  52. package/server/templates/excels/reports/dtbanletheonv.xlsx +0 -0
  53. package/server/templates/excels/reports/dtbanletheoquy.xlsx +0 -0
  54. package/server/templates/excels/reports/dtbanletheothang.xlsx +0 -0
  55. package/server/templates/excels/reports/dtbanletheovt.xlsx +0 -0
  56. package/server/templates/excels/reports/hangbanbitralai.xlsx +0 -0
  57. package/server/templates/excels/reports/hoadonbanhangtheohantt.xlsx +0 -0
  58. package/server/templates/excels/reports/hoadonmuahangtheohantt.xlsx +0 -0
  59. package/server/templates/excels/reports/kqhdkd.xlsx +0 -0
  60. package/server/templates/excels/reports/lcttgt.xlsx +0 -0
  61. package/server/templates/excels/reports/lctttt.xlsx +0 -0
  62. package/server/templates/excels/reports/pttct.xlsx +0 -0
  63. package/server/templates/excels/reports/sctcnkh.xlsx +0 -0
  64. package/server/templates/excels/reports/sctdt.xlsx +0 -0
  65. package/server/templates/excels/reports/scttk.xlsx +0 -0
  66. package/server/templates/excels/reports/sctvt.xlsx +0 -0
  67. package/server/templates/excels/reports/socaitk.xlsx +0 -0
  68. package/server/templates/excels/reports/sochut.xlsx +0 -0
  69. package/server/templates/excels/reports/sonkbh.xlsx +0 -0
  70. package/server/templates/excels/reports/sonkc.xlsx +0 -0
  71. package/server/templates/excels/reports/sonkct.xlsx +0 -0
  72. package/server/templates/excels/reports/sonkmh.xlsx +0 -0
  73. package/server/templates/excels/reports/sonktt.xlsx +0 -0
  74. package/server/templates/excels/reports/soquy.xlsx +0 -0
  75. package/server/templates/excels/reports/sotaisan.xlsx +0 -0
  76. package/server/templates/excels/reports/sotiengui.xlsx +0 -0
  77. package/server/templates/excels/reports/thnxt.xlsx +0 -0
  78. package/server/templates/excels/reports/tkgtgt.xlsx +0 -0
  79. package/server/templates/excels/reports/tonghopbanhang.xlsx +0 -0
  80. package/server/templates/excels/reports/tonghopmuahang.xlsx +0 -0
  81. package/server/templates/excels/reports/tonghoptralaihang.xlsx +0 -0
  82. package/server/templates/imports/cdkh.xlsx +0 -0
  83. package/server/templates/imports/cdtk.xlsx +0 -0
  84. package/server/templates/imports/cdvt.xlsx +0 -0
  85. package/server/templates/imports/contract.xlsx +0 -0
  86. package/server/templates/imports/dmban.xlsx +0 -0
  87. package/server/templates/imports/dmkh.xlsx +0 -0
  88. package/server/templates/imports/dmvt.xlsx +0 -0
  89. package/server/templates/imports/hd1.xlsx +0 -0
  90. package/server/templates/imports/hd2.xlsx +0 -0
  91. package/server/templates/imports/lienhe.xlsx +0 -0
  92. package/server/templates/imports/pc1.xlsx +0 -0
  93. package/server/templates/imports/pkt.xlsx +0 -0
  94. package/server/templates/imports/pn1.xlsx +0 -0
  95. package/server/templates/imports/pn2.xlsx +0 -0
  96. package/server/templates/imports/pnk.xlsx +0 -0
  97. package/server/templates/imports/pt1.xlsx +0 -0
  98. package/server/templates/order.html +42 -0
  99. package/server/templates/order.pug +57 -0
  100. package/server/templates/product-verify-result.ejs +230 -0
  101. package/server/templates/reply-comment.html +10 -0
  102. package/server/templates/reply-comment.pug +12 -0
  103. package/server/templates/reset mat khau.html +17 -0
  104. package/server/templates/reset mat khau.pug +18 -0
  105. package/server/templates/thong tin dang nhap.html +15 -0
  106. package/server/templates/thong tin dang nhap.pug +18 -0
  107. package/server/templates/thu moi gia nhap nhom.html +14 -0
  108. package/server/templates/thu moi gia nhap nhom.pug +18 -0
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "flexbiz-server",
3
3
  "main": "./server/app.js",
4
4
  "description": "Flexible Server",
5
- "version": "12.5.25",
5
+ "version": "12.5.26",
6
6
  "author": {
7
7
  "name": "Van Truong Pham",
8
8
  "email": "invncur@gmail.com"
@@ -10,9 +10,9 @@ fields:["tg_tk","kg_kiem_tra_ton_kho","kg_kiem_tra_ton_kho_tt",{cb:"combo"},"ten
10
10
  return $c$$}):[$detail$$]).reduce(($a$$,$b$$)=>$a$$.concat($b$$),[]);await $details$$.asyncJoinModel2($appInfo$$._id,dmqddvt,{where:{ma_vt:"ma_vt",ma_dvt:"ma_dvt"},fields:"ty_le_qd"});let $data_sokho$$=[];$data$$._id&&($data_sokho$$=await global.getModel("sokho").find({id_ct:$data$$._id.toString()}).lean());$details$$.forEach($d$$=>{$d$$.he_so_qd=$d$$.ty_le_qd||1;$d$$.sl_xuat_qd=$d$$.he_so_qd*$d$$.sl_xuat});$details$$=await $details$$.asyncGroupBy(["ma_vt","ma_kho","ten_vt"],["sl_xuat_qd"]);async.map($details$$,
11
11
  ($d$$,$callback$$)=>{let $ma_kho$$=$d$$.ma_kho||$data$$.ma_kho||$data$$.ma_kho_x,$query$$={ma_vt:$d$$.ma_vt,ma_kho:$ma_kho$$,ngay:new Date,id_app:$appInfo$$._id};$d$$.kg_kiem_tra_ton_kho_tt||($d$$.ma_lo&&($query$$.ma_lo=$d$$.ma_lo),$d$$.han_sd&&($query$$.han_sd=$d$$.han_sd),$d$$.ma_tt1&&($query$$.ma_tt1=$d$$.ma_tt1),$d$$.ma_tt2&&($query$$.ma_tt2=$d$$.ma_tt2),$d$$.ma_tt3&&($query$$.ma_tt3=$d$$.ma_tt3));ckvt($query$$,function($e$$,$rs$$){if($e$$)return $callback$$($e$$);$rs$$?($e$$=$rs$$.csum("ton"),
12
12
  $rs$$=$data_sokho$$.filter($s$$=>$s$$.ma_vt===$d$$.ma_vt&&$s$$.ma_kho===$ma_kho$$).reduce(($a$$,$b$$)=>($a$$.sl_xuat_qd||0)+($b$$.sl_xuat_qd||0),0),$d$$.sl_xuat_qd>utils.round($e$$+$rs$$,2)?$callback$$(`S\u1ea3n ph\u1ea9m ${$d$$.ma_vt} - ${$d$$.ten_vt} c\u00f3 s\u1ed1 l\u01b0\u1ee3ng xu\u1ea5t quy \u0111\u1ed5i (${$d$$.sl_xuat_qd}) l\u1edbn h\u01a1n s\u1ed1 l\u01b0\u1ee3ng t\u1ed3n quy \u0111\u1ed5i (${$e$$+$rs$$})`):$callback$$()):$callback$$()})},$e$$=>{$callback$$($e$$)})})():$callback$$()}},$e$$=>
13
- {$fn$$($e$$)})}async cacheData($obj$$,$callback$$){$obj$$&&$obj$$.toObject&&($obj$$=utils.convertObjectIdsToStrings($obj$$.toObject()));await redisCache.set(this.model_name,$obj$$,$callback$$)}async deleteData($obj$$,$callback$$){$obj$$&&$obj$$.toObject&&($obj$$=utils.convertObjectIdsToStrings($obj$$.toObject()));await redisCache.remove(this.model_name,$obj$$,$callback$$)}createRoute($routeName$$,$handler_routeAction$$,$_options$$={method:"GET",not_use_worker:!1}){const $self$$=this;$_options$$.method=
14
- $_options$$.method?$_options$$.method.toUpperCase():"GET";const $route_action_key$$=`route_action_${$self$$.name}_${$routeName$$}_${$_options$$.method}`;$self$$[$route_action_key$$]=$handler_routeAction$$;$handler_routeAction$$=async function($req$$,$res$$){const {query:$query$$,body:$body$$,user:$user$$,params:$params$$,headers:$headers$$,files:$files$$}=$req$$,$user_agent$$=$req$$.header("user-agent"),$ip$$=$req$$.ip||$req$$.headers["x-forwarded-for"]||$req$$.connection.remoteAddress;try{const $_req$$=
15
- {handler:"createRouteHandler",routeName:$routeName$$,_options:$_options$$,route_action_key:$route_action_key$$,query:$query$$,body:$body$$,user:$user$$,params:$params$$,headers:$headers$$,user_agent:$user_agent$$,ip:$ip$$,files:$files$$,originalUrl:$req$$.originalUrl};global.inputMainPool&&!$_options$$.not_use_worker?global.inputMainPool.exec({req:$_req$$,module:$self$$.name.toUpperCase(),configs:JSON.stringify(configs)},$response$$=>{if($response$$.error)return $res$$.status(400).send({error:$response$$.error.error||
13
+ {$fn$$($e$$)})}async cacheData($obj$$,$callback$$){$obj$$&&$obj$$.toObject&&($obj$$=utils.convertObjectIdsToStrings($obj$$.toObject()));await redisCache.set(this.model_name,$obj$$,$callback$$)}async deleteData($obj$$,$callback$$){$obj$$&&$obj$$.toObject&&($obj$$=utils.convertObjectIdsToStrings($obj$$.toObject()));await redisCache.remove(this.model_name,$obj$$,$callback$$)}createRoute($routeName$$,$handler_routeAction$$,$_options$$={method:"GET",not_use_worker:!1,right_code:"view"}){const $self$$=
14
+ this;$_options$$.method=$_options$$.method?$_options$$.method.toUpperCase():"GET";const $route_action_key$$=`route_action_${$self$$.name}_${$routeName$$}_${$_options$$.method}`;$self$$[$route_action_key$$]=$handler_routeAction$$;$handler_routeAction$$=async function($req$$,$res$$){const {query:$query$$,body:$body$$,user:$user$$,params:$params$$,headers:$headers$$,files:$files$$}=$req$$,$user_agent$$=$req$$.header("user-agent"),$ip$$=$req$$.ip||$req$$.headers["x-forwarded-for"]||$req$$.connection.remoteAddress;
15
+ try{const $_req$$={handler:"createRouteHandler",routeName:$routeName$$,_options:$_options$$,route_action_key:$route_action_key$$,query:$query$$,body:$body$$,user:$user$$,params:$params$$,headers:$headers$$,user_agent:$user_agent$$,ip:$ip$$,files:$files$$,originalUrl:$req$$.originalUrl};global.inputMainPool&&!$_options$$.not_use_worker?global.inputMainPool.exec({req:$_req$$,module:$self$$.name.toUpperCase(),configs:JSON.stringify(configs)},$response$$=>{if($response$$.error)return $res$$.status(400).send({error:$response$$.error.error||
16
16
  $response$$.error.message||$response$$.error,message:$response$$.message||$response$$.error.error||$response$$.error.message||$response$$.error});$res$$.send($response$$.result)}):handlers.createRouteHandler($self$$,$_req$$,($e$$,$rs$$)=>{if($e$$)return $res$$.status(400).send($e$$);$res$$.send($rs$$)})}catch($e$$){$res$$.status(400).send($e$$)}};let $url$$=`${this.route_name}/${$routeName$$}`,$router$$=this.router;$_options$$.method=="DELETE"?$router$$.route($url$$).delete($handler_routeAction$$):
17
17
  $_options$$.method=="POST"?$router$$.route($url$$).post($handler_routeAction$$):$_options$$.method=="PUT"?$router$$.route($url$$).put($handler_routeAction$$):$router$$.route($url$$).get($handler_routeAction$$)}async requestApprove($app_info_listinfo_code$$,$user_request$$,$obj_request$$,$next$$,$str_func$jscomp$2_str_func$$,$preRequest$$){let $ctrl$$=this;if(!$app_info_listinfo_code$$)return $next$$(null,$obj_request$$);let $obj$$;$obj$$=$obj_request$$.toObject?utils.convertObjectIdsToStrings($obj_request$$.toObject()):
18
18
  _.cloneDeep($obj_request$$);$obj$$.ma_ct=$obj$$.ma_ct||$ctrl$$.name;let $approveDatas$$=[];$app_info_listinfo_code$$=$obj$$.listinfo_code||$ctrl$$.name;$str_func$jscomp$2_str_func$$||($str_func$jscomp$2_str_func$$=(await OptionsModel.findOne({id_app:$obj$$.id_app,id_func:$app_info_listinfo_code$$},{option:1}).lean()||{}).option);$str_func$jscomp$2_str_func$$&&$str_func$jscomp$2_str_func$$.custom_approve_user&&$str_func$jscomp$2_str_func$$.custom_approve_user.forEach($trang_thai_approve$$=>{let $update_after_approve$$,
@@ -24,8 +24,8 @@ $res$$.setHeader("Content-Length",$data$jscomp$2_data$jscomp$3_response$jscomp$3
24
24
  $res$$.end($data$jscomp$4_data$jscomp$5_returnvalue$$);break;case "docx":$res$$.setHeader("Content-Type","application/vnd.openxmlformats-officedocument.wordprocessingml.document");$res$$.setHeader("Content-Disposition",'attachment; filename="'+$rptId$$+'".docx');$data$jscomp$4_data$jscomp$5_returnvalue$$=$e$jscomp$7_result$$;$data$jscomp$4_data$jscomp$5_returnvalue$$?.type==="Buffer"&&Array.isArray($data$jscomp$4_data$jscomp$5_returnvalue$$.data)&&($data$jscomp$4_data$jscomp$5_returnvalue$$=Buffer.from($data$jscomp$4_data$jscomp$5_returnvalue$$.data));
25
25
  $res$$.setHeader("Content-Length",$data$jscomp$4_data$jscomp$5_returnvalue$$.length);$res$$.end($data$jscomp$4_data$jscomp$5_returnvalue$$);break;default:$res$$.send($e$jscomp$7_result$$)}})}catch($e$$){$res$$.status(400).send($e$$)}};$router$$.route(`${this.base_path+this.module}`).get($mainRoute$$);$router$$.route(`${this.base_path+this.module}`).post($mainRoute$$);$router$$.route(`${this.base_path+this.module}/excel`).get($excelRoute$$);$router$$.route(`${this.base_path+this.module}/excel`).post($excelRoute$$)}getData($req$$,
26
26
  $callback$$){const $ctrl$$=this;let $callback_run$$=!1;setImmediate(()=>{try{$ctrl$$.fecthDataFunc($req$$,($err$$,$data$$,$event$$)=>{if($ctrl$$.options?.stream){if($err$$)return $callback$$($err$$);$event$$!="data"||$callback_run$$||($callback_run$$=!0,this.handleResult($req$$,{error:$err$$,result:$data$$},($e$$,$rs$$)=>{$callback$$($e$$,$rs$$)}))}else $callback_run$$=!0,this.handleResult($req$$,{error:$err$$,result:$data$$},($e$$,$rs$$)=>{$callback$$($e$$,$rs$$)})})}catch($e$$){console.error("[report controller] [getData]",
27
- $e$$),$callback$$($e$$.message)}})}createRoute($routeName$$,$callbackRoute_handler$$,$_options$$={method:"GET",not_use_worker:!1}){const $self$$=this;$_options$$.method=$_options$$.method?$_options$$.method.toUpperCase():"GET";const $route_action_key$$=`rpt_route_action_${$self$$.module}_${$routeName$$}_${$_options$$.method}`;$self$$[$route_action_key$$]=$callbackRoute_handler$$;$callbackRoute_handler$$=async function($ip$jscomp$2_req$$,$res$$){const {query:$query$$,body:$body$$,user:$user$$,params:$params$$,
28
- files:$files$$}=$ip$jscomp$2_req$$;var $_req$jscomp$2_user_agent$$=$ip$jscomp$2_req$$.header("user-agent");$ip$jscomp$2_req$$=$ip$jscomp$2_req$$.ip||$ip$jscomp$2_req$$.headers["x-forwarded-for"]||$ip$jscomp$2_req$$.connection.remoteAddress;try{if($_req$jscomp$2_user_agent$$={handler:"rptCreateRouteHandler",routeName:$routeName$$,_options:$_options$$,route_action_key:$route_action_key$$,query:$query$$,body:$body$$,user:$user$$,params:$params$$,user_agent:$_req$jscomp$2_user_agent$$,ip:$ip$jscomp$2_req$$,
29
- files:$files$$},!global.reportMainPool||global.reportMainPool.fullQueue()||$_options$$.not_use_worker)handlers.rptCreateRouteHandler(this,$_req$jscomp$2_user_agent$$,($e$$,$returnvalue$$)=>{if($e$$)return $res$$.status(400).send($e$$);$res$$.send($returnvalue$$)});else{const $id_task$$=`report-create-route-${crypto.randomBytes(20).toString("hex")}`;global.reportMainPool.exec({id_task:$id_task$$,req:$_req$jscomp$2_user_agent$$,module:$self$$.module.toUpperCase(),configs:JSON.stringify(configs)},$response$jscomp$6_result$$=>
30
- {if($response$jscomp$6_result$$.error)return $res$$.status(400).send($response$jscomp$6_result$$.error);({result:$response$jscomp$6_result$$}=$response$jscomp$6_result$$);$res$$.send($response$jscomp$6_result$$)})}}catch($e$$){$res$$.status(400).send($e$$)}};$_options$$.method=="DELETE"?this.router.route(`${this.base_path+this.module}/${$routeName$$}`).delete($callbackRoute_handler$$):$_options$$.method=="POST"?this.router.route(`${this.base_path+this.module}/${$routeName$$}`).post($callbackRoute_handler$$):
27
+ $e$$),$callback$$($e$$.message)}})}createRoute($routeName$$,$callbackRoute_handler$$,$_options$$={method:"GET",not_use_worker:!1,right_code:"view"}){const $self$$=this;$_options$$.method=$_options$$.method?$_options$$.method.toUpperCase():"GET";const $route_action_key$$=`rpt_route_action_${$self$$.module}_${$routeName$$}_${$_options$$.method}`;$self$$[$route_action_key$$]=$callbackRoute_handler$$;$callbackRoute_handler$$=async function($ip$jscomp$2_req$$,$res$$){const {query:$query$$,body:$body$$,
28
+ user:$user$$,params:$params$$,files:$files$$}=$ip$jscomp$2_req$$;var $_req$jscomp$2_user_agent$$=$ip$jscomp$2_req$$.header("user-agent");$ip$jscomp$2_req$$=$ip$jscomp$2_req$$.ip||$ip$jscomp$2_req$$.headers["x-forwarded-for"]||$ip$jscomp$2_req$$.connection.remoteAddress;try{if($_req$jscomp$2_user_agent$$={handler:"rptCreateRouteHandler",routeName:$routeName$$,_options:$_options$$,route_action_key:$route_action_key$$,query:$query$$,body:$body$$,user:$user$$,params:$params$$,user_agent:$_req$jscomp$2_user_agent$$,
29
+ ip:$ip$jscomp$2_req$$,files:$files$$},!global.reportMainPool||global.reportMainPool.fullQueue()||$_options$$.not_use_worker)handlers.rptCreateRouteHandler(this,$_req$jscomp$2_user_agent$$,($e$$,$returnvalue$$)=>{if($e$$)return $res$$.status(400).send($e$$);$res$$.send($returnvalue$$)});else{const $id_task$$=`report-create-route-${crypto.randomBytes(20).toString("hex")}`;global.reportMainPool.exec({id_task:$id_task$$,req:$_req$jscomp$2_user_agent$$,module:$self$$.module.toUpperCase(),configs:JSON.stringify(configs)},
30
+ $response$jscomp$6_result$$=>{if($response$jscomp$6_result$$.error)return $res$$.status(400).send($response$jscomp$6_result$$.error);({result:$response$jscomp$6_result$$}=$response$jscomp$6_result$$);$res$$.send($response$jscomp$6_result$$)})}}catch($e$$){$res$$.status(400).send($e$$)}};$_options$$.method=="DELETE"?this.router.route(`${this.base_path+this.module}/${$routeName$$}`).delete($callbackRoute_handler$$):$_options$$.method=="POST"?this.router.route(`${this.base_path+this.module}/${$routeName$$}`).post($callbackRoute_handler$$):
31
31
  $_options$$.method=="PUT"?this.router.route(`${this.base_path+this.module}/${$routeName$$}`).put($callbackRoute_handler$$):this.router.route(`${this.base_path+this.module}/${$routeName$$}`).get($callbackRoute_handler$$)}}module.exports=controllerRPT;
@@ -1,4 +1,4 @@
1
1
  const permission=require("../libs/permission"),{ERRORS}=require("./controllerUtils"),createRouteHandler=async($ctrl$$,$req$$,$callback$$)=>{let $res_send$$=!1;const {routeName:$routeName$$,route_action_key:$route_action_key$$,_options:$_options$$}=$req$$;if($ctrl$$.require_id_app===!1)try{$ctrl$$[$route_action_key$$]($req$$,function($e$$,$rs$$){if($res_send$$)return console.error("route",$routeName$$,"sent header. callback had call");if($e$$){console.error("create route",$routeName$$,$e$$);if($e$$.error)return $res_send$$=
2
- !0,$callback$$($e$$);$res_send$$=!0;return $callback$$({error:$e$$.message||$e$$})}$res_send$$=!0;return $callback$$(null,$rs$$)})}catch($e$$){return console.error("route",$routeName$$,$e$$),$res_send$$=!0,$callback$$({error:$e$$.message||$e$$})}else{let $id_app$$=$req$$.params.id_app,$notNeedRight$$=$_options$$.notNeedRight||await $ctrl$$.notNeedRight($req$$.user);permission.hasRight($id_app$$,$req$$.user.email,$ctrl$$.module,"view",function($error$$,$hr$$){if($res_send$$)return console.error("route",
2
+ !0,$callback$$($e$$);$res_send$$=!0;return $callback$$({error:$e$$.message||$e$$})}$res_send$$=!0;return $callback$$(null,$rs$$)})}catch($e$$){return console.error("route",$routeName$$,$e$$),$res_send$$=!0,$callback$$({error:$e$$.message||$e$$})}else{let $id_app$$=$req$$.params.id_app,$notNeedRight$$=$_options$$.notNeedRight||await $ctrl$$.notNeedRight($req$$.user);permission.hasRight($id_app$$,$req$$.user.email,$ctrl$$.module,$_options$$?.right_code||"view",function($error$$,$hr$$){if($res_send$$)return console.error("route",
3
3
  $routeName$$,"sent header. hasRight had call");if($hr$$)try{$ctrl$$[$route_action_key$$]($req$$,function($e$$,$rs$$){$res_send$$&&console.error("route",$routeName$$,"sent header. callback had call");if($e$$){if($e$$.error)return $res_send$$=!0,$callback$$($e$$);$res_send$$=!0;return $callback$$({error:$e$$.message||$e$$})}$res_send$$=!0;return $callback$$(null,$rs$$)})}catch($e$$){return $res_send$$=!0,console.error("route",$routeName$$,$e$$),$callback$$({error:$e$$.message||$e$$})}else return $res_send$$=
4
4
  !0,$callback$$({error:$error$$.error||$error$$||ERRORS.ERR_NOT_PERMIT,code:$error$$.code||ERRORS.ERR_NOT_PERMIT_CODE})},{notNeedRight:$notNeedRight$$})}};module.exports=createRouteHandler;
@@ -1,4 +1,4 @@
1
1
  const permission=require("../libs/permission"),createRouteHandler=async($ctrl$$,$req$$,$callback$$)=>{const {routeName:$routeName$$,route_action_key:$route_action_key$$,_options:$_options$$}=$req$$;let $notNeedRight$$=$_options$$.notNeedRight||await $ctrl$$.notNeedRight($req$$.user),$res_send$$=!1;if($ctrl$$.options.require_id_app===!1)try{$ctrl$$[$route_action_key$$]($req$$,function($e$$,$rs$$){if($res_send$$)return console.error("no id_app route",$routeName$$,"sent header. calback had call");if($e$$){console.error("handle create route ",
2
- $routeName$$,$e$$);if($e$$.error)return $res_send$$=!0,$callback$$($e$$);$res_send$$=!0;return $callback$$({error:$e$$.message||$e$$})}$res_send$$=!0;return $callback$$(null,$rs$$)})}catch($e$$){return $res_send$$=!0,$callback$$({error:$e$$.message||$e$$})}else permission.hasRight($req$$.params.id_app,$req$$.user.email,$ctrl$$.module,"view",function($error$$,$hr$$){if($res_send$$)return console.error("route",$routeName$$,"sent header. hasRight had call");if($hr$$)try{$ctrl$$[$route_action_key$$]($req$$,
2
+ $routeName$$,$e$$);if($e$$.error)return $res_send$$=!0,$callback$$($e$$);$res_send$$=!0;return $callback$$({error:$e$$.message||$e$$})}$res_send$$=!0;return $callback$$(null,$rs$$)})}catch($e$$){return $res_send$$=!0,$callback$$({error:$e$$.message||$e$$})}else permission.hasRight($req$$.params.id_app,$req$$.user.email,$ctrl$$.module,$_options$$?.right_code||"view",function($error$$,$hr$$){if($res_send$$)return console.error("route",$routeName$$,"sent header. hasRight had call");if($hr$$)try{$ctrl$$[$route_action_key$$]($req$$,
3
3
  function($e$$,$rs$$){if($res_send$$)return console.error("route",$routeName$$,"sent header. calback had call");if($e$$){console.error("handle create route ",$routeName$$,$e$$);if($e$$.error)return $res_send$$=!0,$callback$$($e$$);$res_send$$=!0;return $callback$$({error:$e$$.message||$e$$})}$res_send$$=!0;return $callback$$(null,$rs$$)})}catch($e$$){return $callback$$({error:$e$$.message||$e$$})}else $res_send$$=!0,$callback$$({error:"B\u1ea1n kh\u00f4ng c\u00f3 quy\u1ec1n xem b\u00e1o c\u00e1o n\u00e0y"})},
4
4
  {notNeedRight:$notNeedRight$$})};module.exports=createRouteHandler;
@@ -27,11 +27,12 @@ $newBatchCacheKey_val$$}else $remainingManualQueries$$.push($mq$$)});$manualQuer
27
27
  function mergeFields($plan$$,$newFields$$){$plan$$.fields!==null&&($newFields$$===void 0?$plan$$.fields=null:($plan$$.fields?Object.assign($plan$$.fields,$newFields$$):$plan$$.fields={...$newFields$$},$plan$$.dynamicKey&&($plan$$.fields[$plan$$.dynamicKey]=1)))}
28
28
  async function executeQueries($batchPlan$$,$manualQueries$$,$options$$,$id_app$$){const $fetchedCache$$={};await runWithoutSession(async()=>{const $promises$$=[];Object.values($batchPlan$$).forEach($plan$$=>{const $values$$=Array.from($plan$$.values).filter($v$$=>$v$$!=null);$values$$.length&&$promises$$.push((async()=>{try{const $modelName$$=$plan$$.model.modelName,$isCacheEnabled$$=$options$$.cache&&cachedKeys&&cachedKeys[$modelName$$];let $missingValues$$=[...$values$$];if($isCacheEnabled$$&&global.clientRedis){const $redisMap$$=
29
29
  $values$$.map($val$$=>{var $rKey_tempQ$$={...$plan$$.staticQuery};$rKey_tempQ$$[$plan$$.dynamicKey]=$val$$;$rKey_tempQ$$=generateRedisKey($modelName$$,$rKey_tempQ$$,$id_app$$);return{val:$val$$,rKey:$rKey_tempQ$$}}),$keysToGet$$=$redisMap$$.map($i$$=>$i$$.rKey),$results$$=await redisMGet($keysToGet$$)||[];$missingValues$$=[];$results$$.forEach(($jsonStr$$,$i$$)=>{$i$$=$redisMap$$[$i$$].val;if($jsonStr$$)try{const $data$$=JSON.parse($jsonStr$$);$data$$._id&&($data$$._id=$data$$._id.toString());const $cacheKey$$=
30
- `${$plan$$.signature}_VAL_${$i$$==null?"__NULL__":$plan$$.dynamicKey==="_id"?$i$$.toString():$i$$}`.replace(/\./g,"_");$fetchedCache$$[$cacheKey$$]=$data$$}catch($e$$){console.error("[joinData] parse d\u1eef li\u1ec7u t\u1eeb redis",$e$$),$missingValues$$.push($i$$)}else $missingValues$$.push($i$$)})}$plan$$.dynamicKey==="_id"&&($missingValues$$=$missingValues$$.filter($m$$=>isValidObjectId($m$$)));if($missingValues$$.length){const $query$$={...$plan$$.staticQuery,[$plan$$.dynamicKey]:{$in:$missingValues$$}};
31
- $id_app$$&&$plan$$.model.schema.paths.id_app&&($query$$.id_app=$id_app$$);let $mq$$=$plan$$.model.find($query$$);$plan$$.fields&&($mq$$=$mq$$.select($plan$$.fields));let $dbResults$$;try{$dbResults$$=await $mq$$.lean()}catch($e$$){throw console.error("[joinData] Batch Error with query:",JSON.stringify($query$$,null,2)),$e$$;}$dbResults$$.forEach($row$$=>{$row$$._id&&($row$$._id=$row$$._id.toString());var $cacheKey$jscomp$4_val$$=$row$$[$plan$$.dynamicKey];$cacheKey$jscomp$4_val$$=`${$plan$$.signature}_VAL_${$cacheKey$jscomp$4_val$$==
32
- null?"__NULL__":$plan$$.dynamicKey==="_id"?$cacheKey$jscomp$4_val$$.toString():$cacheKey$jscomp$4_val$$}`.replace(/\./g,"_");$fetchedCache$$[$cacheKey$jscomp$4_val$$]=$row$$})}}catch($e$$){console.error("[joinData] Batch Error:",$e$$.message)}})())});const $pendingRequests$$=new Map;$manualQueries$$.forEach($mq$$=>{const $key$$=$mq$$.cacheKey;if($pendingRequests$$.has($key$$))$promises$$.push($pendingRequests$$.get($key$$));else{var $executionPromise$$=(async()=>{try{let $data$$=null;if($options$$.cache&&
33
- cachedKeys&&cachedKeys[$mq$$.modelName]&&global.clientRedis){const $jsonStr$$=await redisGet($mq$$.cacheKey);if($jsonStr$$)try{$data$$=JSON.parse($jsonStr$$)}catch($e$$){console.warn("JSON parse error from Redis:",$mq$$.cacheKey)}}if(!$data$$){console.log("[joinData][manualQueries]",$mq$$.model.modelName,$mq$$.query);let $query$$=$mq$$.model.findOne($mq$$.query);$mq$$.fields&&($query$$=$query$$.select($mq$$.fields));$data$$=await $query$$.lean()}$data$$?($data$$._id&&($data$$._id=$data$$._id.toString()),
34
- $fetchedCache$$[$mq$$.cacheKey]=$data$$):$fetchedCache$$[$mq$$.cacheKey]=null}catch($e$$){console.error("[joinData][executeQueries] Manual Query Error:",$e$$),$fetchedCache$$[$mq$$.cacheKey]=null}finally{$pendingRequests$$.delete($key$$)}})();$pendingRequests$$.set($key$$,$executionPromise$$);$promises$$.push($executionPromise$$)}});await Promise.all($promises$$)});return $fetchedCache$$}
30
+ `${$plan$$.signature}_VAL_${$i$$==null?"__NULL__":$plan$$.dynamicKey==="_id"?$i$$.toString():$i$$}`.replace(/\./g,"_");$fetchedCache$$[$cacheKey$$]=$data$$}catch($e$$){console.warn("\u26a0\ufe0f [joinData] parse d\u1eef li\u1ec7u t\u1eeb redis",$e$$),$missingValues$$.push($i$$)}else $missingValues$$.push($i$$)})}$plan$$.dynamicKey==="_id"&&($missingValues$$=$missingValues$$.filter($m$$=>isValidObjectId($m$$)));if($missingValues$$.length){const $query$$={...$plan$$.staticQuery,[$plan$$.dynamicKey]:{$in:$missingValues$$}};
31
+ $id_app$$&&$plan$$.model.schema.paths.id_app&&($query$$.id_app=$id_app$$);let $mq$$=$plan$$.model.find($query$$);$plan$$.fields&&($mq$$=$mq$$.select($plan$$.fields));let $dbResults$$;try{$dbResults$$=await $mq$$.lean()}catch($e$$){throw console.error("\u274c[joinData] Batch Error with query:",JSON.stringify($query$$,null,2)),$e$$;}$dbResults$$.forEach($row$$=>{$row$$._id&&($row$$._id=$row$$._id.toString());var $cacheKey$jscomp$4_val$$=$row$$[$plan$$.dynamicKey];$cacheKey$jscomp$4_val$$=`${$plan$$.signature}_VAL_${$cacheKey$jscomp$4_val$$==
32
+ null?"__NULL__":$plan$$.dynamicKey==="_id"?$cacheKey$jscomp$4_val$$.toString():$cacheKey$jscomp$4_val$$}`.replace(/\./g,"_");$fetchedCache$$[$cacheKey$jscomp$4_val$$]=$row$$})}}catch($e$$){console.error("\u274c[joinData] Batch Error:",$e$$.message)}})())});const $pendingRequests$$=new Map;$manualQueries$$.forEach($mq$$=>{const $key$$=$mq$$.cacheKey;if($pendingRequests$$.has($key$$))$promises$$.push($pendingRequests$$.get($key$$));else{var $executionPromise$$=(async()=>{try{let $data$$=null;if($options$$.cache&&
33
+ cachedKeys&&cachedKeys[$mq$$.modelName]&&global.clientRedis){const $jsonStr$$=await redisGet($mq$$.cacheKey);if($jsonStr$$)try{$data$$=JSON.parse($jsonStr$$)}catch($e$$){console.warn("\u26a0\ufe0f [joinData][executeQueries][manualQueries] JSON parse error from Redis:",$mq$$.cacheKey)}}if(!$data$$)if(Object.values($mq$$.query).filter($v$$=>$v$$===void 0||$v$$===null).length>0)console.warn("\u26a0\ufe0f [joinData][executeQueries][manualQueries]",$mq$$.model.modelName,": query kh\u00f4ng h\u1ee3p l\u1ec7 do c\u00f3 tr\u01b0\u1eddng c\u00f3 gi\u00e1 tr\u1ecb ==null or undefined",
34
+ $mq$$.query);else{console.log("[joinData][manualQueries]",$mq$$.model.modelName,$mq$$.query);let $query$$=$mq$$.model.findOne($mq$$.query);$mq$$.fields&&($query$$=$query$$.select($mq$$.fields));$data$$=await $query$$.lean()}$data$$?($data$$._id&&($data$$._id=$data$$._id.toString()),$fetchedCache$$[$mq$$.cacheKey]=$data$$):$fetchedCache$$[$mq$$.cacheKey]=null}catch($e$$){console.error("\u274c[joinData][executeQueries] Manual Query Error:",$e$$),$fetchedCache$$[$mq$$.cacheKey]=null}finally{$pendingRequests$$.delete($key$$)}})();
35
+ $pendingRequests$$.set($key$$,$executionPromise$$);$promises$$.push($executionPromise$$)}});await Promise.all($promises$$)});return $fetchedCache$$}
35
36
  async function mapResultsToItems($data$jscomp$2_items$$,$metaMap$$,$dataCache$$){for(let $item$$ of $data$jscomp$2_items$$){$data$jscomp$2_items$$=$metaMap$$.get($item$$)||[];for(let $meta$$ of $data$jscomp$2_items$$){const {join:$join$$,cacheKey:$cacheKey$$}=$meta$$;$data$jscomp$2_items$$=($data$jscomp$2_items$$=$dataCache$$[$cacheKey$$])||null;await applyFields($item$$,$data$jscomp$2_items$$||{},$join$$)}}}
36
37
  async function applyFields($item$$,$data$$,$join$$){if($data$$)try{$join$$.fields&&(lodash.isFunction($join$$.fields)?$join$$.fields($item$$,$data$$):(Array.isArray($join$$.fields)?$join$$.fields:[$join$$.fields]).forEach($map$$=>{if(lodash.isObject($map$$))if($map$$.name&&$map$$.value)$item$$[$map$$.name]=$data$$[$map$$.value];else for(let $key_map$$ in $map$$)$item$$[$key_map$$]=$data$$[$map$$[$key_map$$]];else $item$$[$map$$]=$data$$[$map$$]})),$join$$.setFields&&$join$$.setFields($item$$,$data$$),
37
38
  $join$$.asyncSetFields&&await $join$$.asyncSetFields($item$$,$data$$,()=>{}).catch(console.error)}catch($e$$){console.error("[joinData] [applyFields]",$e$$.message,JSON.stringify({item:$item$$,data:$data$$,join:$join$$},null,2))}}
@@ -1,3 +1,3 @@
1
- const mailaccountSchema=new Schema({id_app:{type:String,required:!0,maxlength:1024},id_link:{type:String},fullname:{type:String,required:!0},username:{type:String,required:!0},password:{type:String,required:!0},imap:{host:{type:String},port:{type:Number,default:993},secure:{type:Boolean,default:!0},mailbox:{type:String,default:"INBOX"},fetchsenders:{type:String,default:"invoice,hoadon"}},smtp:{host:{type:String,required:!0},port:{type:Number,default:465,required:!0},ssl:{type:Boolean,default:!0}},
2
- error:{},status_string:{type:String},status_code:Number,provider:{type:String,default:"generic"},last_checked:{type:Date,default:null},status:{type:Boolean,default:!0},date_created:{type:Date,default:Date.now},date_updated:{type:Date,default:Date.now},user_created:{type:String,default:""},user_updated:{type:String,default:""}});
1
+ const imapSchema=new Schema({host:{type:String},port:{type:Number,default:993},secure:{type:Boolean,default:!0},mailbox:{type:String,default:"INBOX"},fetchsenders:{type:String,default:"invoice,hoadon"}},{_id:!1}),smtpSchema=new Schema({host:{type:String},port:{type:Number,default:465},ssl:{type:Boolean,default:!0}},{_id:!1}),mailaccountSchema=new Schema({id_app:{type:String,required:!0,maxlength:1024},id_link:{type:String},fullname:{type:String,required:!0},username:{type:String,required:!0},password:{type:String,
2
+ required:!0},imap:imapSchema,smtp:smtpSchema,error:{},status_string:{type:String},status_code:Number,provider:{type:String,default:"generic"},last_checked:{type:Date,default:null},status:{type:Boolean,default:!0},date_created:{type:Date,default:Date.now},date_updated:{type:Date,default:Date.now},user_created:{type:String,default:""},user_updated:{type:String,default:""}});
3
3
  (global.configs||{}).createIndexes&&(mailaccountSchema.index({id_app:1,username:"text",fullname:"text"}),mailaccountSchema.index({id_app:1,username:1}),mailaccountSchema.index({id_app:1,fullname:1}),mailaccountSchema.index({id_app:1,id_link:1}),mailaccountSchema.index({id_app:1,status:1}),mailaccountSchema.index({id_app:1,user_created:1,visible_to:1,visible_to_users:1}));const model=mongoose.models.mailaccount||mongoose.model("mailaccount",mailaccountSchema);module.exports=model;
@@ -0,0 +1,2 @@
1
+ const productCodeSchema=new Schema({id_app:{type:String,required:!0,maxlength:1024},code:{type:String,required:!0},dmvt:{type:Schema.Types.ObjectId,ref:"dmvt",required:!0},is_scanned:{type:Boolean,default:!1},scan_count:{type:Number,default:0},scan_history:[{scanned_at:{type:Date,default:Date.now},ip:String,user_agent:String}],qr_image:String,status:{type:Boolean,default:!0},date_created:{type:Date,default:Date.now},date_updated:{type:Date,default:Date.now},user_created:{type:String,default:""},user_updated:{type:String,
2
+ default:""}});module.exports=mongoose.model("productcode",productCodeSchema);(global.configs||{}).createIndexes&&(productCodeSchema.index({id_app:1,code:1},{unique:!0}),productCodeSchema.index({id_app:1,dmvt:1}),productCodeSchema.index({id_app:1,dmvt:1,code:1}),productCodeSchema.index({id_app:1,user_created:1,visible_to:1,visible_to_users:1}));
@@ -0,0 +1,7 @@
1
+ const model=global.getModel("productcode"),controller=require("../../controllers/controller"),crypto=require("crypto"),qr=require("qr-image"),ejs=require("ejs"),path=require("path"),viewsDir=path.join(__dirname,"../../templates"),renderView=($data$$,$templateName$$="product-verify-result.ejs")=>new Promise(($resolve$$,$reject$$)=>{const $filePath$$=path.join(viewsDir,$templateName$$);ejs.renderFile($filePath$$,$data$$,($err$$,$str$$)=>{if($err$$)return $reject$$($err$$);$resolve$$($str$$)})});
2
+ module.exports=function($contr_router$$){$contr_router$$=new controller($contr_router$$,model,"productcode",{unique:["code"],notNeedRight:($user$$,$options$$={})=>($options$$.action||"").toLowerCase()=="view"?!0:!1,onView:async($user$$,$items$$,$next$$)=>{await $items$$.asyncJoinModel2($user$$.current_id_app,"dmvt",{where:{dmvt:"_id"},fields:["ma_vt","ten_vt","picture"]});$next$$(null,$items$$)}});$contr_router$$.createRoute("create-codes/:ma_vt/:quantity",async($req$$,$next$$)=>{try{var $createdCodes_ma_vt$$=
3
+ $req$$.params.ma_vt;const $quantity$$=Number($req$$.params.quantity||1),$product$$=await mongoose.model("dmvt").findOne({ma_vt:$createdCodes_ma_vt$$,id_app:$req$$.user.current_id_app}).lean();if(!$product$$)return $next$$({error:"S\u1ea3n ph\u1ea9m kh\u00f4ng t\u1ed3n t\u1ea1i"});$createdCodes_ma_vt$$=[];for(let $i$$=0;$i$$<($quantity$$||1);$i$$++){const $uniqueString$$=crypto.randomBytes(6).toString("hex"),$qrImage$$=`data:image/png;base64,${qr.imageSync(`${configs.api_url||configs.domain}/api/${$req$$.user.current_id_app}/productcode/verify/${$uniqueString$$}?access_token=${configs.public_token}`,
4
+ {type:"png",margin:2}).toString("base64")}`;await model.create({id_app:$req$$.user.current_id_app,code:$uniqueString$$,dmvt:$product$$._id,qr_image:$qrImage$$});$createdCodes_ma_vt$$.push({uniqueString:$uniqueString$$,qrImage:$qrImage$$})}$next$$(null,{success:!0,count:$createdCodes_ma_vt$$.length})}catch($error$$){$next$$({error:$error$$.message})}},{right_code:"add"});$contr_router$$.createRoute("verify/:code",async($req$$,$next$$)=>{const {code:$code$$}=$req$$.params,$clientIp$$=$req$$.headers["x-forwarded-for"]||
5
+ $req$$.socket.remoteAddress,$userAgent$$=$req$$.headers["user-agent"];try{const $codeInfo$$=await model.findOne({code:$code$$,id_app:$req$$.user.current_id_app}).populate("dmvt");let $renderData$$;$codeInfo$$?($codeInfo$$.scan_history.push({ip:$clientIp$$,user_agent:$userAgent$$}),$codeInfo$$.scan_count+=1,$codeInfo$$.is_scanned?(await $codeInfo$$.save(),$renderData$$={status:"WARNING",code:$code$$,company:$req$$.user.current_app_info||{},scanCount:$codeInfo$$.scan_count,product:$codeInfo$$.dmvt,
6
+ message:`C\u1ea3nh b\u00e1o: S\u1ea3n ph\u1ea9m n\u00e0y \u0111\u00e3 \u0111\u01b0\u1ee3c k\u00edch ho\u1ea1t tr\u01b0\u1edbc \u0111\u00f3 v\u00e0o ng\u00e0y ${$codeInfo$$.scan_history[0].scanned_at.toLocaleString()}. N\u1ebfu b\u1ea1n v\u1eeba m\u1edbi b\u00f3c tem, c\u00f3 th\u1ec3 \u0111\u00e2y l\u00e0 h\u00e0ng gi\u1ea3.`}):($codeInfo$$.is_scanned=!0,await $codeInfo$$.save(),$renderData$$={status:"GENUINE",code:$code$$,company:$req$$.user.current_app_info||{},product:$codeInfo$$.dmvt,scanCount:$codeInfo$$.scan_count,
7
+ message:"Ch\u00fac m\u1eebng! S\u1ea3n ph\u1ea9m ch\u00ednh h\u00e3ng v\u00e0 \u0111\u01b0\u1ee3c k\u00edch ho\u1ea1t l\u1ea7n \u0111\u1ea7u ti\u00ean."})):$renderData$$={status:"FAKE",code:$code$$,company:$req$$.user.current_app_info||{},scanCount:0,message:"M\u00e3 s\u1ea3n ph\u1ea9m kh\u00f4ng t\u1ed3n t\u1ea1i tr\u00ean h\u1ec7 th\u1ed1ng."};const $html$$=await renderView($renderData$$);$next$$(null,$html$$)}catch($error$$){console.error("[productcode]",$error$$),$next$$("System Error")}});$contr_router$$.route()};
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>
3
+ <title>{{program}}</title>
4
+ </head>
5
+ <body>
6
+ <h3>{{user.name}}<{{user.email}}> đã thêm một file tại <a href='{{file.url_topic}}'>'{{file.url_topic}}'</a></h3>
7
+ </body>
8
+ </html>
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ title=program
4
+ </head>
5
+ <body>
6
+ h3=user.name+'<'+user.email+'> đã thêm một file tại '
7
+ a(href=file.url_topic)=file.url_topic
8
+ </body>
9
+ </html>
@@ -0,0 +1,11 @@
1
+ <html>
2
+ <head>
3
+ <title>{{ten_cv}}</title>
4
+ </head>
5
+ <body>
6
+ <h3>{{ten_cv}}</h3>
7
+ <p>Nội dung công việc: {{mieu_ta}}</p>
8
+ <p>Ngày bắt đầu: {{start_date}}</p>
9
+ <p>Khách hàng: {{ten_kh}}</p>
10
+ </body>
11
+ </html>
@@ -0,0 +1,12 @@
1
+ <html>
2
+ <head>
3
+ title=ten_cv
4
+ </head>
5
+ <body>
6
+ h3=ten_cv
7
+ p
8
+ !='Nội dung công việc: ' + mieu_ta
9
+ p='Ngày bắt đầu: ' + moment(start_date).format("DD/MM/YYYY")
10
+ p='Khách hàng: ' + ten_kh
11
+ </body>
12
+ </html>
@@ -0,0 +1,15 @@
1
+ <html>
2
+ <head>
3
+ <title>Thông báo hợp đồng hết hạn bảo hành</title>
4
+ </head>
5
+ <body>
6
+ <h3>{{company.name}} thông báo hợp đồng sau đã hết hạn bảo hành</h3>
7
+ <p><b>Hợp đồng:</b> {{contract.ten_hd}}, số: {{contract.so_hd}}</p>
8
+ <p><b>Ngày ký:</b> {{contract.ngay_hd###}}</p>
9
+ <p><b>Khách hàng:</b> {{customer.ten_kh}}</p>
10
+ <p><b>Địa chỉ:</b> {{customer.dia_chi}}</p>
11
+ <p><b>Điện thoại:</b> {{customer.dien_thoai}}</p>
12
+ <p><b>Ngày bảo hành:</b> {{start_date###}}</p>
13
+ <p><b>Thời gian bảo hành:</b> {{warranty_time}} {{unit_time_name}}</p>
14
+ </body>
15
+ </html>
@@ -0,0 +1,28 @@
1
+ <html>
2
+ <head>
3
+ <title>Thông báo hợp đồng hết hạn bảo hành</title>
4
+ </head>
5
+ body
6
+ h3=company.name+' thông báo hợp đồng sau đã hết hạn bảo hành'
7
+ p
8
+ b Hợp đồng:
9
+ !=contract.ten_hd+', số: ' + contract.so_hd
10
+ p
11
+ b Ngày ký:
12
+ !=moment(contract.ngay_hd).format("DD/MM/YYYY")
13
+ p
14
+ b Khách hàng:
15
+ !=customer.ten_kh
16
+ p
17
+ b Địa chỉ:
18
+ !=customer.dia_chi
19
+ p
20
+ b Điện thoại:
21
+ !=customer.dien_thoai
22
+ p
23
+ b Ngày bảo hành:
24
+ !=moment(start_date).format("DD/MM/YYYY")
25
+ p
26
+ b Thời gian bảo hành:
27
+ !=warranty_time.toString() + ' ' + unit_time_name
28
+ </html>
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,42 @@
1
+ <html>
2
+ <head>
3
+ <title>Đơn đặt hàng</title>
4
+ </head>
5
+ <body>
6
+ <p>Đơn hàng được gửi tới từ <a href='{{app.site_name}}'>{{app.site_name}}</a>.</p>
7
+ <hr/>
8
+ <div>
9
+ <h1>ĐƠN ĐẶT HÀNG</h1>
10
+ <p>Số: <strong>{{order.so_ct}}</strong> - Ngày: <strong>{{order.ngay_ct}}</strong></p>
11
+ <p>Người nhận hàng: <strong>{{order.ten_nguoi_nhan}}</strong></p>
12
+ <p>Điện thoại: <strong>{{order.dien_thoai}}</strong></p>
13
+ <p>Email: <strong>{{order.email}}</strong></p>
14
+ <p>Địa chỉ: <strong>{{order.dia_chi}}, {{order.xa_phuong}}, {{order.quan_huyen}}, {{order.tinh_thanh}}</strong></p>
15
+
16
+ <p style='color:red'>Phương thức thanh toán: <strong>{{order.ten_pt_thanh_toan}}</strong></p>
17
+ <p>
18
+ <table style="border-collapse: collapse;border:1px solid gray;width:100%">
19
+ <tr>
20
+ <th style="border:1px solid gray;width:100px">Mã sản phẩm</th>
21
+ <th style="border:1px solid gray;width:250px">Tên sản phẩm</th>
22
+ <th style="border:1px solid gray;width:100px">Đơn vị tính</th>
23
+ <th style="border:1px solid gray;width:100px">Số lượng</th>
24
+ <th style="border:1px solid gray;width:100px">Giá</th>
25
+ <th style="border:1px solid gray;width:100px">Thành tiền</th>
26
+ </tr>
27
+ {{order.rows}}
28
+ <tr>
29
+ <td style="border:1px solid gray"></td>
30
+ <td style="border:1px solid gray"></td>
31
+ <td style="border:1px solid gray"><b>Tổng cộng</b></td>
32
+ <td style="border:1px solid gray"><b>{{order.t_sl}}</b></td>
33
+ <td style="border:1px solid gray"></td>
34
+ <td style="border:1px solid gray"><b>{{order.t_tt}}</b></td>
35
+ </tr>
36
+ </table>
37
+ </p>
38
+
39
+
40
+ </div>
41
+ </body>
42
+ </html>
@@ -0,0 +1,57 @@
1
+ <html>
2
+ <head>
3
+ <title>Đơn đặt hàng</title>
4
+ </head>
5
+ <body>
6
+ p='Đơn hàng được gửi tới từ '
7
+ a(href=app.site_name)=app.site_name
8
+ <hr/>
9
+ div
10
+ h1 ĐƠN ĐẶT HÀNG
11
+ p
12
+ !='Số: <strong>'+order.so_ct+'</strong> - Ngày: <strong>'+moment(order.ngay_ct).format('DD/MM/YYYY')+'</strong>'
13
+ p
14
+ !='Người nhận hàng: <strong>'+order.ten_nguoi_nhan+'</strong>'
15
+ p
16
+ !='Điện thoại: <strong>'+ order.dien_thoai+'</strong>'
17
+ p
18
+ !='Email: <strong>'+order.email+'</strong>'
19
+ p
20
+ !='Địa chỉ: <strong>' + order.dia_chi+', ' + order.xa_phuong + ', ' + order.quan_huyen + ', ' + order.tinh_thanh + '</strong>'
21
+
22
+ p(style='color:red')
23
+ !='Phương thức thanh toán: <strong>' + order.ten_pt_thanh_toan + '</strong>'
24
+ table(style="border-collapse: collapse;border:1px solid gray;width:100%")
25
+ tr
26
+ th(style="border:1px solid gray;width:100px") Mã sản phẩm
27
+ th(style="border:1px solid gray;width:250px") Tên sản phẩm
28
+ th(style="border:1px solid gray;width:100px") Đơn vị tính
29
+ th(style="border:1px solid gray;width:100px") Số lượng
30
+ th(style="border:1px solid gray;width:100px") Giá
31
+ th(style="border:1px solid gray;width:100px") Thành tiền
32
+ each row in order.details
33
+ tr
34
+ td(style='border:1px solid gray')=row.ma_vt
35
+ td(style='border:1px solid gray')=row.ten_vt
36
+ td(style='border:1px solid gray')=row.ma_dvt
37
+ td(style='border:1px solid gray')=numeral(row.sl_xuat).format("0,0.0")
38
+ td(style='border:1px solid gray')=numeral(row.gia_ban_thuc).format("0,0")
39
+ td(style='border:1px solid gray')=numeral(row.tien).format("0,0")
40
+ tr
41
+ td(style="border:1px solid gray")
42
+ td(style="border:1px solid gray")
43
+ td(style="border:1px solid gray")
44
+ b Tổng cộng
45
+ td(style="border:1px solid gray")
46
+ b=numeral(order.t_sl).format("0,0.0")
47
+ td(style="border:1px solid gray")
48
+ td(style="border:1px solid gray")
49
+ b=numeral(order.t_tt).format("0,0")
50
+
51
+
52
+
53
+
54
+
55
+
56
+ </body>
57
+ </html>
@@ -0,0 +1,230 @@
1
+ <!DOCTYPE html>
2
+ <html lang="vi">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>Kết quả xác thực sản phẩm</title>
7
+ <style>
8
+ :root {
9
+ --color-success: #2ecc71;
10
+ --color-warning: #f39c12;
11
+ --color-danger: #e74c3c;
12
+ --color-text: #2c3e50;
13
+ --color-bg: #f4f6f8;
14
+ --font-main: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
15
+ }
16
+
17
+ * { box-sizing: border-box; margin: 0; padding: 0; }
18
+
19
+ body {
20
+ font-family: var(--font-main);
21
+ background-color: var(--color-bg);
22
+ color: var(--color-text);
23
+ line-height: 1.6;
24
+ display: flex;
25
+ justify-content: center;
26
+ min-height: 100vh;
27
+ }
28
+
29
+ .container {
30
+ width: 100%;
31
+ max-width: 480px; /* Giới hạn chiều rộng như app mobile */
32
+ background: white;
33
+ display: flex;
34
+ flex-direction: column;
35
+ box-shadow: 0 0 20px rgba(0,0,0,0.05);
36
+ position: relative;
37
+ }
38
+
39
+ /* HEADER TRẠNG THÁI */
40
+ .status-header {
41
+ padding: 40px 20px;
42
+ text-align: center;
43
+ color: white;
44
+ border-bottom-left-radius: 30px;
45
+ border-bottom-right-radius: 30px;
46
+ margin-bottom: 20px;
47
+ }
48
+
49
+ .status-icon {
50
+ font-size: 80px;
51
+ margin-bottom: 15px;
52
+ display: block;
53
+ }
54
+
55
+ .status-title {
56
+ font-size: 24px;
57
+ font-weight: 800;
58
+ text-transform: uppercase;
59
+ letter-spacing: 1px;
60
+ margin-bottom: 5px;
61
+ }
62
+
63
+ .status-desc {
64
+ font-size: 16px;
65
+ opacity: 0.9;
66
+ }
67
+
68
+ /* PRODUCT INFO */
69
+ .product-card {
70
+ padding: 0 25px;
71
+ text-align: center;
72
+ }
73
+
74
+ .product-image-wrapper {
75
+ width: 200px;
76
+ height: 200px;
77
+ margin: -60px auto 20px; /* Kéo ảnh lên đè lên header */
78
+ background: white;
79
+ border-radius: 20px;
80
+ padding: 10px;
81
+ box-shadow: 0 10px 25px rgba(0,0,0,0.1);
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ }
86
+
87
+ .product-image {
88
+ max-width: 100%;
89
+ max-height: 100%;
90
+ object-fit: contain;
91
+ border-radius: 10px;
92
+ }
93
+
94
+ .product-name {
95
+ font-size: 20px;
96
+ font-weight: 700;
97
+ margin-bottom: 5px;
98
+ color: #333;
99
+ }
100
+
101
+ .product-code {
102
+ color: #7f8c8d;
103
+ font-size: 14px;
104
+ background: #eee;
105
+ padding: 4px 12px;
106
+ border-radius: 15px;
107
+ display: inline-block;
108
+ margin-bottom: 20px;
109
+ }
110
+
111
+ /* DETAILS BOX */
112
+ .detail-box {
113
+ margin: 20px;
114
+ padding: 20px;
115
+ background: #fff;
116
+ border: 1px solid #eee;
117
+ border-radius: 15px;
118
+ }
119
+
120
+ .detail-row {
121
+ display: flex;
122
+ justify-content: space-between;
123
+ margin-bottom: 12px;
124
+ padding-bottom: 12px;
125
+ border-bottom: 1px solid #f5f5f5;
126
+ }
127
+
128
+ .detail-row:last-child {
129
+ border-bottom: none;
130
+ margin-bottom: 0;
131
+ padding-bottom: 0;
132
+ }
133
+
134
+ .label { color: #95a5a6; font-size: 14px; }
135
+ .value { font-weight: 600; font-size: 15px; text-align: right; }
136
+
137
+ /* BUTTON ACTION */
138
+ .action-area {
139
+ margin-top: auto;
140
+ padding: 20px;
141
+ text-align: center;
142
+ }
143
+
144
+ .btn {
145
+ display: block;
146
+ width: 100%;
147
+ padding: 15px;
148
+ border-radius: 12px;
149
+ text-decoration: none;
150
+ font-weight: bold;
151
+ font-size: 16px;
152
+ transition: opacity 0.2s;
153
+ }
154
+
155
+ .btn-support {
156
+ background-color: #f1f2f6;
157
+ color: #333;
158
+ }
159
+
160
+ /* LOGIC MÀU SẮC */
161
+ /* Class động dựa trên biến status truyền vào */
162
+ .theme-GENUINE .status-header { background-color: var(--color-success); }
163
+ .theme-WARNING .status-header { background-color: var(--color-warning); }
164
+ .theme-FAKE .status-header { background-color: var(--color-danger); }
165
+
166
+ </style>
167
+ </head>
168
+ <body class="theme-<%= status %>">
169
+
170
+ <div class="container">
171
+
172
+ <div class="status-header">
173
+ <span class="status-icon">
174
+ <% if (status === 'GENUINE') { %>
175
+ <svg width="80" height="80" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
176
+ <% } else if (status === 'WARNING') { %>
177
+ <svg width="80" height="80" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
178
+ <% } else { %>
179
+ <svg width="80" height="80" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>
180
+ <% } %>
181
+ </span>
182
+
183
+ <div class="status-title">
184
+ <% if (status === 'GENUINE') { %> CHÍNH HÃNG <% } %>
185
+ <% if (status === 'WARNING') { %> CẢNH BÁO <% } %>
186
+ <% if (status === 'FAKE') { %> KHÔNG RÕ NGUỒN GỐC <% } %>
187
+ </div>
188
+ <div class="status-desc"><%= message %></div>
189
+ </div>
190
+
191
+ <% if (status !== 'FAKE' && typeof product !== 'undefined') { %>
192
+ <div class="product-card">
193
+ <div class="product-image-wrapper">
194
+ <img src="<%= product.picture %>" alt="Product" class="product-image" onerror="this.src='https://via.placeholder.com/200?text=No+Image'">
195
+ </div>
196
+ <h2 class="product-name"><%= product.ten_vt %></h2>
197
+ <span class="product-code">Mã SP: <%= product.ma_vt %></span>
198
+ </div>
199
+
200
+ <div class="detail-box">
201
+ <% if (status === 'WARNING') { %>
202
+ <div class="detail-row" style="color: var(--color-danger)">
203
+ <span class="label" style="color: inherit">Số lần quét:</span>
204
+ <span class="value"><%= scanCount %> lần</span>
205
+ </div>
206
+ <% } %>
207
+
208
+ <div class="detail-row">
209
+ <span class="label">Ngày xác thực:</span>
210
+ <span class="value"><%= new Date().toLocaleDateString('vi-VN') %></span>
211
+ </div>
212
+ <div class="detail-row">
213
+ <span class="label">Serial:</span>
214
+ <span class="value">...</span>
215
+ </div>
216
+ </div>
217
+ <% } %>
218
+
219
+ <div class="action-area">
220
+ <% if (status === 'GENUINE') { %>
221
+ <a href="#" class="btn" style="background: var(--color-success); color: white;">Xem chi tiết sản phẩm</a>
222
+ <% } else { %>
223
+ <a href="tel:1900xxxx" class="btn btn-support">Liên hệ hỗ trợ: 1900 xxxx</a>
224
+ <% } %>
225
+ <p style="margin-top: 15px; font-size: 12px; color: #999;">Hệ thống xác thực chống hàng giả</p>
226
+ </div>
227
+
228
+ </div>
229
+ </body>
230
+ </html>
@@ -0,0 +1,10 @@
1
+ <html>
2
+ <head>
3
+ <title>{{program}}</title>
4
+ </head>
5
+ <body>
6
+ <h3>{{user.name}}<{{user.email}}> đã trả lời vào chủ đề <a href='{{comment.url_topic}}'>'{{comment.title}}'</a></h3>
7
+
8
+ <p>{{comment.content}}</p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,12 @@
1
+ <html>
2
+ <head>
3
+ title=program
4
+ </head>
5
+ <body>
6
+ h3=user.name+'<'+user.email+'> đã trả lời vào chủ đề '
7
+ a(href=comment.url_topic)=comment.title
8
+
9
+ p
10
+ !=comment.content
11
+ </body>
12
+ </html>
@@ -0,0 +1,17 @@
1
+ <html>
2
+ <head>
3
+ <title>{{program}}</title>
4
+ </head>
5
+ <body>
6
+ <p>Xin chào {{receiver_name}},</p>
7
+ <p>Đây là thông tin mới dùng để đăng nhập vào tài khoản của bạn</p>
8
+ <p><b>Tên đăng nhập:</b>{{email}}</p>
9
+ <p><b>Mật khẩu:</b>{{password}}</p>
10
+ <p>Sau khi đăng nhập thành công, bạn nên đổi lại mật khẩu trên.</p>
11
+ <p>Nếu bạn không phải là người đã yêu cầu đổi mật khẩu thì bạn có thể bỏ qua email này, mật khẩu của bạn vẫn giữ nguyên.</p>
12
+ <p></p>
13
+ <p>Trân trọng,</p>
14
+ <p>{{company}}</p>
15
+
16
+ </body>
17
+ </html>
@@ -0,0 +1,18 @@
1
+ <html>
2
+ <head>
3
+ title=program
4
+ </head>
5
+ <body>
6
+ p='Xin chào '+receiver_name
7
+ p='Đây là thông tin mới dùng để đăng nhập vào tài khoản của bạn'
8
+ p
9
+ !='<b>Tên đăng nhập:</b>'+email
10
+ p
11
+ !='<b>Mật khẩu:</b>'+password
12
+ p Sau khi đăng nhập thành công, bạn nên đổi lại mật khẩu trên.
13
+ p Nếu bạn không phải là người đã yêu cầu đổi mật khẩu thì bạn có thể bỏ qua email này, mật khẩu của bạn vẫn giữ nguyên.
14
+ p
15
+ p Trân trọng,
16
+ p=company
17
+ </body>
18
+ </html>
@@ -0,0 +1,15 @@
1
+ <html>
2
+ <head>
3
+ <title>{{program}}</title>
4
+ </head>
5
+ <body>
6
+ <p>Xin chào {{receiver_name}},</p>
7
+ <p>Đây là thông tin dùng để đăng nhập vào tài khoản của bạn trên chương trình <a href='{{domain}}'>{{domain}}</a></p>
8
+ <p><b>Tên đăng nhập:</b>{{email}}</p>
9
+ <p><b>Mật khẩu:</b>{{password}}</p>
10
+ <p>Sau khi đăng nhập thành công, bạn nên đổi lại mật khẩu trên.</p>
11
+ <p></p>
12
+ <p>Trân trọng,</p>
13
+ <p>{{company}}</p>
14
+ </body>
15
+ </html>
@@ -0,0 +1,18 @@
1
+ <html>
2
+ <head>
3
+ title=program
4
+ </head>
5
+ <body>
6
+ p='Xin chào '+receiver_name+','
7
+ p='Đây là thông tin dùng để đăng nhập vào tài khoản của bạn trên chương trình '
8
+ a(href=domain)=domain
9
+ p
10
+ !='<b>Tên đăng nhập:</b>'+email
11
+ p
12
+ !='<b>Mật khẩu:</b>'+password
13
+ p Sau khi đăng nhập thành công, bạn nên đổi lại mật khẩu trên.
14
+ p
15
+ p Trân trọng,
16
+ p=company
17
+ </body>
18
+ </html>
@@ -0,0 +1,14 @@
1
+ <html>
2
+ <head>
3
+ <title>{{program}}</title>
4
+ </head>
5
+ <body>
6
+ <div>Xin chào, {{receiver_name}}</div>
7
+ <p><b>{{sender_name}}</b> đã mời bạn tham gia nhóm làm việc tại <a href='{{domain}}'>{{domain}}</a></p>
8
+ <p>Để chấp nhận tham gia, bạn hãy đăng nhập vào chương trình tại <a href='{{domain}}'>{{domain}}</a></p>
9
+ <p></p>
10
+ <p>Trân trọng,</p>
11
+ <p>{{company}}</p>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,18 @@
1
+ <html>
2
+ <head>
3
+ title=program
4
+ </head>
5
+ <body>
6
+ div='Xin chào, ' + receiver_name
7
+ p
8
+ b=sender_name
9
+ |đã mời bạn tham gia nhóm làm việc tại
10
+ a(href=domain)=domain
11
+ p Để chấp nhận tham gia, bạn hãy đăng nhập vào chương trình tại
12
+ a(href=domain)=domain
13
+ p
14
+ p Trân trọng,
15
+ p=company
16
+
17
+ </body>
18
+ </html>