Commit b50927a5ce01c796a5159778ff47259d5b035c3d

Authored by 李宇
2 parents ad3658d9 3f8b942f

Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP

Showing 30 changed files with 2993 additions and 1677 deletions
antis-ncc-admin/package-lock.json
... ... @@ -21699,7 +21699,7 @@
21699 21699 },
21700 21700 "node_modules/xlsx": {
21701 21701 "version": "0.18.5",
21702   - "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
  21702 + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
21703 21703 "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
21704 21704 "dependencies": {
21705 21705 "adler-32": "~1.3.0",
... ... @@ -39759,7 +39759,7 @@
39759 39759 },
39760 39760 "xlsx": {
39761 39761 "version": "0.18.5",
39762   - "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
  39762 + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
39763 39763 "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
39764 39764 "requires": {
39765 39765 "adler-32": "~1.3.0",
... ...
antis-ncc-admin/src/views/lqHytkHytk/Form.vue
1 1 <template>
2   - <el-dialog :title="!dataForm.id ? '新建' : isDetail ? '详情':'编辑'" :close-on-click-modal="false" :visible.sync="visible" class="NCC-dialog NCC-dialog_center" lock-scroll width="90%">
3   - <el-row :gutter="15" class="" >
4   - <el-form ref="elForm" :model="dataForm" size="small" label-width="100px" label-position="right" :disabled="!!isDetail" :rules="rules">
5   - <el-col :span="8" v-if="false" >
6   - <el-form-item label="退卡编号" prop="id">
7   - <el-input v-model="dataForm.id" placeholder="请输入" clearable :style='{"width":"100%"}' >
8   - </el-input>
9   - </el-form-item>
10   - </el-col>
11   - <el-col :span="8">
12   - <el-form-item label="门店" prop="md" required>
13   - <el-select v-model="dataForm.md" placeholder="请选择" clearable :style='{"width":"100%"}' filterable @change="handleMdChange">
14   - <el-option v-for="(item, index) in mdOptions" :key="index" :label="item.fullName" :value="item.id" ></el-option>
15   - </el-select>
16   - </el-form-item>
17   - </el-col>
18   - <el-col :span="8">
  2 + <el-dialog :title="!dataForm.id ? '新建' : isDetail ? '详情' : '编辑'" :close-on-click-modal="false"
  3 + :visible.sync="visible" class="NCC-dialog NCC-dialog_center" lock-scroll width="90%">
  4 + <el-row :gutter="15" class="">
  5 + <el-form ref="elForm" :model="dataForm" size="small" label-width="100px" label-position="right"
  6 + :disabled="!!isDetail" :rules="rules">
  7 + <el-col :span="8" v-if="false">
  8 + <el-form-item label="退卡编号" prop="id">
  9 + <el-input v-model="dataForm.id" placeholder="请输入" clearable :style='{ "width": "100%" }'>
  10 + </el-input>
  11 + </el-form-item>
  12 + </el-col>
  13 + <el-col :span="8">
  14 + <el-form-item label="门店" prop="md" required>
  15 + <el-select v-model="dataForm.md" placeholder="请选择" clearable :style='{ "width": "100%" }'
  16 + filterable @change="handleMdChange">
  17 + <el-option v-for="(item, index) in mdOptions" :key="index" :label="item.fullName"
  18 + :value="item.id"></el-option>
  19 + </el-select>
  20 + </el-form-item>
  21 + </el-col>
  22 + <el-col :span="8">
19 23 <el-form-item label="退卡时间" prop="tksj">
20   - <el-date-picker v-model="dataForm.tksj" placeholder="请选择" clearable :style='{"width":"100%"}' type='date' format="yyyy-MM-dd" value-format="timestamp" >
  24 + <el-date-picker v-model="dataForm.tksj" placeholder="请选择" clearable :style='{ "width": "100%" }'
  25 + type='date' format="yyyy-MM-dd" value-format="timestamp">
21 26 </el-date-picker>
22 27 </el-form-item>
23 28 </el-col>
24   - <el-col :span="8" v-if="false">
25   - <el-form-item label="门店编号" prop="mdbh">
26   - <el-input v-model="dataForm.mdbh" placeholder="自动填充" clearable readonly :style='{"width":"100%"}' >
27   - </el-input>
28   - </el-form-item>
29   - </el-col>
30   - <el-col :span="8" v-if="false">
31   - <el-form-item label="门店名称" prop="mdmc">
32   - <el-input v-model="dataForm.mdmc" placeholder="自动填充" clearable readonly :style='{"width":"100%"}' >
33   - </el-input>
34   - </el-form-item>
35   - </el-col>
36   - <el-col :span="8">
37   - <el-form-item label="会员" prop="hy" required>
38   - <el-select v-model="dataForm.hy" placeholder="请选择" clearable :style='{"width":"100%"}' filterable @change="handleHyChange">
39   - <el-option v-for="(item, index) in hyOptions" :key="index" :label="item.fullName" :value="item.id" ></el-option>
40   - </el-select>
41   - </el-form-item>
42   - </el-col>
43   - <el-col :span="8" v-if="false">
44   - <el-form-item label="会员姓名" prop="hymc">
45   - <el-input v-model="dataForm.hymc" placeholder="自动填充" clearable readonly :style='{"width":"100%"}' >
46   - </el-input>
47   - </el-form-item>
48   - </el-col>
49   - <el-col :span="8" v-if="false">
50   - <el-form-item label="会员账号" prop="hyzh">
51   - <el-input v-model="dataForm.hyzh" placeholder="自动填充" clearable readonly :style='{"width":"100%"}' >
52   - </el-input>
53   - </el-form-item>
54   - </el-col>
55   - <el-col :span="8">
56   - <el-form-item label="退款金额" prop="tkje">
57   - <el-input v-model="dataForm.tkje" placeholder="自动计算" clearable readonly :style='{"width":"100%"}'>
58   - <template slot="append">元</template>
59   - </el-input>
60   - </el-form-item>
61   - </el-col>
62   - <el-col :span="8">
63   - <el-form-item label="手工费用" prop="sgfy">
64   - <el-input v-model="dataForm.sgfy" placeholder="自动计算" clearable readonly :style='{"width":"100%"}'>
65   - <template slot="append">元</template>
66   - </el-input>
67   - </el-form-item>
68   - </el-col>
69   - <el-col :span="16"></el-col>
70   - <el-col :span="24" v-if="false" >
71   - <el-form-item label="退卡品项" prop="tkpx">
72   - <el-input v-model="dataForm.tkpx" placeholder="请输入" clearable :style='{"width":"100%"}' >
73   - </el-input>
74   - </el-form-item>
75   - </el-col>
76   - <el-col :span="24" v-if="dataForm.hy && pxOptions.length > 0">
77   - <el-form-item label="会员剩余品项" label-width="100px">
78   - <div class="member-remaining-items">
79   - <div class="remaining-items-header">
80   - <span class="items-title">会员剩余品项列表 ({{ pxOptions.length }} 个)</span>
81   - </div>
82   - <div class="remaining-items-list">
83   - <div v-for="(item, index) in pxOptions" :key="index" class="remaining-item-row" :class="{ 'disabled': item.RemainingCount <= 0 }">
84   - <div class="item-name">{{ item.fullName }}</div>
85   - <div class="item-price">¥{{ item.ItemPrice || 0 }}</div>
86   - <div class="item-quantity" :class="{ 'zero': item.RemainingCount <= 0 }">
87   - 剩余: {{ item.RemainingCount || 0 }}
88   - </div>
89   - <div class="item-source">{{ item.sourceType || '购买' }}</div>
  29 + <el-col :span="8" v-if="false">
  30 + <el-form-item label="门店编号" prop="mdbh">
  31 + <el-input v-model="dataForm.mdbh" placeholder="自动填充" clearable readonly
  32 + :style='{ "width": "100%" }'>
  33 + </el-input>
  34 + </el-form-item>
  35 + </el-col>
  36 + <el-col :span="8" v-if="false">
  37 + <el-form-item label="门店名称" prop="mdmc">
  38 + <el-input v-model="dataForm.mdmc" placeholder="自动填充" clearable readonly
  39 + :style='{ "width": "100%" }'>
  40 + </el-input>
  41 + </el-form-item>
  42 + </el-col>
  43 + <el-col :span="8">
  44 + <el-form-item label="会员" prop="hy" required>
  45 + <el-select v-model="dataForm.hy" placeholder="请选择" clearable :style='{ "width": "100%" }'
  46 + filterable @change="handleHyChange">
  47 + <el-option v-for="(item, index) in hyOptions" :key="index" :label="item.fullName"
  48 + :value="item.id"></el-option>
  49 + </el-select>
  50 + </el-form-item>
  51 + </el-col>
  52 + <el-col :span="8" v-if="false">
  53 + <el-form-item label="会员姓名" prop="hymc">
  54 + <el-input v-model="dataForm.hymc" placeholder="自动填充" clearable readonly
  55 + :style='{ "width": "100%" }'>
  56 + </el-input>
  57 + </el-form-item>
  58 + </el-col>
  59 + <el-col :span="8" v-if="false">
  60 + <el-form-item label="会员账号" prop="hyzh">
  61 + <el-input v-model="dataForm.hyzh" placeholder="自动填充" clearable readonly
  62 + :style='{ "width": "100%" }'>
  63 + </el-input>
  64 + </el-form-item>
  65 + </el-col>
  66 + <el-col :span="8">
  67 + <el-form-item label="退款金额" prop="tkje">
  68 + <el-input v-model="dataForm.tkje" placeholder="自动计算" clearable readonly
  69 + :style='{ "width": "100%" }'>
  70 + <template slot="append">元</template>
  71 + </el-input>
  72 + </el-form-item>
  73 + </el-col>
  74 + <el-col :span="8">
  75 + <el-form-item label="手工费用" prop="sgfy">
  76 + <el-input v-model="dataForm.sgfy" placeholder="自动计算" clearable readonly
  77 + :style='{ "width": "100%" }'>
  78 + <template slot="append">元</template>
  79 + </el-input>
  80 + </el-form-item>
  81 + </el-col>
  82 + <el-col :span="16"></el-col>
  83 + <el-col :span="24" v-if="false">
  84 + <el-form-item label="退卡品项" prop="tkpx">
  85 + <el-input v-model="dataForm.tkpx" placeholder="请输入" clearable :style='{ "width": "100%" }'>
  86 + </el-input>
  87 + </el-form-item>
  88 + </el-col>
  89 + <el-col :span="24" v-if="dataForm.hy && pxOptions.length > 0">
  90 + <el-form-item label="会员剩余品项" label-width="100px">
  91 + <div class="member-remaining-items">
  92 + <div class="remaining-items-header">
  93 + <span class="items-title">会员剩余品项列表 ({{ pxOptions.length }} 个)</span>
  94 + </div>
  95 + <div class="remaining-items-list">
  96 + <div v-for="(item, index) in pxOptions" :key="index" class="remaining-item-row"
  97 + :class="{ 'disabled': item.RemainingCount <= 0 }">
  98 + <div class="item-name">{{ item.fullName }}</div>
  99 + <div class="item-price">¥{{ item.ItemPrice || 0 }}</div>
  100 + <div class="item-quantity" :class="{ 'zero': item.RemainingCount <= 0 }">
  101 + 剩余: {{ item.RemainingCount || 0 }}
90 102 </div>
  103 + <div class="item-source">{{ item.sourceType || '购买' }}</div>
91 104 </div>
92 105 </div>
93   - </el-form-item>
94   - </el-col>
95   - <el-col :span="24">
  106 + </div>
  107 + </el-form-item>
  108 + </el-col>
  109 + <el-col :span="24">
96 110 <el-form-item label-width="0">
97 111 <div class="table-header">
98 112 <span class="table-title">品项明细</span>
99   - <el-button v-if="!dataForm.id" type="warning" size="mini" icon="el-icon-plus" @click="addHandleLqXhPxmxEntityList()">新增品项</el-button>
  113 + <el-button v-if="!dataForm.id" type="warning" size="mini" icon="el-icon-plus"
  114 + @click="addHandleLqXhPxmxEntityList()">新增品项</el-button>
100 115 </div>
101   -
  116 +
102 117 <!-- 品项列表 -->
103 118 <div class="px-list-container">
104 119 <div v-for="(px, pxIndex) in dataForm.lqHytkMxList" :key="pxIndex" class="px-item">
... ... @@ -106,16 +121,20 @@
106 121 <div class="px-basic-info">
107 122 <div class="px-item-header">
108 123 <span class="px-item-title">品项明细</span>
109   - <el-button v-if="!dataForm.id"size="mini" type="danger" icon="el-icon-delete" @click="handleDelLqXhPxmxEntityList(pxIndex)">删除</el-button>
  124 + <el-button v-if="!dataForm.id" size="mini" type="danger" icon="el-icon-delete"
  125 + @click="handleDelLqXhPxmxEntityList(pxIndex)">删除</el-button>
110 126 </div>
111   -
  127 +
112 128 <!-- 品项选择 -->
113 129 <div class="px-select-row">
114   - <el-select v-model="px.pxid" placeholder="请选择品项" clearable filterable @change="handlePxChange(pxIndex, px)" style="width: 200px;">
115   - <el-option v-for="(item, index) in pxOptions" :key="index" :label="item.fullName+'(剩余: '+(item.RemainingCount || 0)+',类型:'+item.sourceType+')'" :value="item.pxid" :disabled="item.disabled"></el-option>
  130 + <el-select v-model="px.pxid" placeholder="请选择品项" clearable filterable
  131 + @change="handlePxChange(pxIndex, px)" style="width: 200px;">
  132 + <el-option v-for="(item, index) in pxOptions" :key="index"
  133 + :label="item.fullName + '(剩余: ' + (item.RemainingCount || 0) + ',类型:' + item.sourceType + ')'"
  134 + :value="item.pxid" :disabled="item.disabled"></el-option>
116 135 </el-select>
117 136 </div>
118   -
  137 +
119 138 <!-- 品项详细信息 -->
120 139 <div class="px-detail-info" v-if="px.px">
121 140 <div class="px-detail-row">
... ... @@ -149,13 +168,17 @@
149 168 </div>
150 169 <div class="px-detail-item">
151 170 <span class="px-detail-label">总业绩:</span>
152   - <span class="px-detail-value">{{ (px.projectNumber || 1) * (px.pxjg || 0) }}</span>
  171 + <span class="px-detail-value">{{ (px.projectNumber || 1) * (px.pxjg ||
  172 + 0) }}</span>
153 173 </div>
154 174 </div>
155   -
  175 +
156 176 <!-- 消费数量输入 -->
157 177 <div class="px-consumption-row">
158   - <el-input-number v-model="px.projectNumber" :min="1" :max="getRemainingCount(px)" placeholder="消费数量" size="mini" style="width: 120px;" @change="onPxProjectNumberChange(pxIndex, $event)"></el-input-number>
  178 + <el-input-number v-model="px.projectNumber" :min="1"
  179 + :max="getRemainingCount(px)" placeholder="消费数量" size="mini"
  180 + style="width: 120px;"
  181 + @change="onPxProjectNumberChange(pxIndex, $event)"></el-input-number>
159 182 </div>
160 183 </div>
161 184 </div>
... ... @@ -164,36 +187,48 @@
164 187 <div class="px-staff-section">
165 188 <div class="px-staff-header">
166 189 <span class="px-staff-title">健康师业绩</span>
167   - <el-button v-if="!dataForm.id" size="mini" type="success" icon="el-icon-plus" @click="addPxJks(pxIndex)">添加健康师</el-button>
  190 + <el-button v-if="!dataForm.id" size="mini" type="success" icon="el-icon-plus"
  191 + @click="addPxJks(pxIndex)">添加健康师</el-button>
168 192 </div>
169 193 <div class="px-staff-list">
170   - <div v-for="(jks, jksIndex) in px.lqHytkJksyjList" :key="jksIndex" class="px-staff-row">
  194 + <div v-for="(jks, jksIndex) in px.lqHytkJksyjList" :key="jksIndex"
  195 + class="px-staff-row">
171 196 <div class="px-staff-item">
172 197 <div class="px-staff-fields">
173 198 <div class="px-staff-field">
174 199 <label>健康师</label>
175   - <el-select v-model="jks.jks" placeholder="选择健康师" clearable filterable size="mini" style="width: 200px;" @change="handleJksChange(pxIndex, jksIndex, jks)">
176   - <el-option v-for="(item, index) in jksOptions" :key="index" :label="item.fullName" :value="item.id"></el-option>
  200 + <el-select v-model="jks.jks" placeholder="选择健康师" clearable
  201 + filterable size="mini" style="width: 200px;"
  202 + @change="handleJksChange(pxIndex, jksIndex, jks)">
  203 + <el-option v-for="(item, index) in jksOptions" :key="index"
  204 + :label="item.fullName" :value="item.id"></el-option>
177 205 </el-select>
178 206 </div>
179 207 <div class="px-staff-field">
180 208 <label>业绩</label>
181   - <el-input v-model="jks.jksyj" placeholder="请输入业绩" size="mini" style="width: 120px;" @input="formatPxStaffNumber(pxIndex, jksIndex, 'jksyj', 'lqHytkJksyjList')">
  209 + <el-input v-model="jks.jksyj" placeholder="请输入业绩" size="mini"
  210 + style="width: 120px;"
  211 + @input="formatPxStaffNumber(pxIndex, jksIndex, 'jksyj', 'lqHytkJksyjList')">
182 212 <template slot="append">元</template>
183 213 </el-input>
184 214 </div>
185 215 <div class="px-staff-field" v-show="false">
186 216 <label>手工费</label>
187   - <el-input v-model="jks.laborCost" placeholder="手工费" size="mini" style="width: 120px;" @input="formatPxStaffNumber(pxIndex, jksIndex, 'laborCost', 'lqHytkJksyjList')">
  217 + <el-input v-model="jks.laborCost" placeholder="手工费" size="mini"
  218 + style="width: 120px;"
  219 + @input="formatPxStaffNumber(pxIndex, jksIndex, 'laborCost', 'lqHytkJksyjList')">
188 220 <template slot="append">元</template>
189 221 </el-input>
190 222 </div>
191 223 <div class="px-staff-field">
192 224 <label>次数</label>
193   - <el-input-number v-model="jks.kdpxNumber" :min="1" :max="999" placeholder="次数" size="mini" style="width: 120px;" @change="onJksNumberChange(pxIndex, jksIndex, $event)"></el-input-number>
  225 + <el-input-number v-model="jks.kdpxNumber" :min="1" :max="999"
  226 + placeholder="次数" size="mini" style="width: 120px;"
  227 + @change="onJksNumberChange(pxIndex, jksIndex, $event)"></el-input-number>
194 228 </div>
195 229 <div class="px-staff-field" v-if="!dataForm.id">
196   - <el-button size="mini" type="danger" icon="el-icon-delete" @click="removePxJks(pxIndex, jksIndex)">删除</el-button>
  230 + <el-button size="mini" type="danger" icon="el-icon-delete"
  231 + @click="removePxJks(pxIndex, jksIndex)">删除</el-button>
197 232 </div>
198 233 </div>
199 234 </div>
... ... @@ -202,46 +237,60 @@
202 237 </div>
203 238  
204 239 <!-- 科技部老师业绩区域 - 仅当品项类型为科美时显示 -->
205   - <div class="px-staff-section" v-if="px.qt2 === '科美'" :key="'kjb-' + pxIndex + '-' + px.qt2">
  240 + <div class="px-staff-section" v-if="px.qt2 === '科美'"
  241 + :key="'kjb-' + pxIndex + '-' + px.qt2">
206 242 <div class="px-staff-header">
207 243 <span class="px-staff-title">科技部老师业绩</span>
208   - <el-button v-if="!dataForm.id" size="mini" type="success" icon="el-icon-plus" @click="addPxKjb(pxIndex)">添加科技部老师</el-button>
  244 + <el-button v-if="!dataForm.id" size="mini" type="success" icon="el-icon-plus"
  245 + @click="addPxKjb(pxIndex)">添加科技部老师</el-button>
209 246 </div>
210 247 <div class="px-staff-list">
211   - <div v-for="(kjb, kjbIndex) in px.lqHytkKjbsyjList" :key="kjbIndex" class="px-staff-row">
  248 + <div v-for="(kjb, kjbIndex) in px.lqHytkKjbsyjList" :key="kjbIndex"
  249 + class="px-staff-row">
212 250 <div class="px-staff-item">
213 251 <div class="px-staff-fields">
214 252 <div class="px-staff-field">
215 253 <label>科技部老师</label>
216   - <el-select v-model="kjb.kjbls" placeholder="选择科技部老师" clearable filterable size="mini" style="width: 200px;" @change="handleKjbChange(pxIndex, kjbIndex, kjb)">
217   - <el-option v-for="(item, index) in kjbOptions" :key="index" :label="item.fullName" :value="item.id"></el-option>
  254 + <el-select v-model="kjb.kjbls" placeholder="选择科技部老师" clearable
  255 + filterable size="mini" style="width: 200px;"
  256 + @change="handleKjbChange(pxIndex, kjbIndex, kjb)">
  257 + <el-option v-for="(item, index) in kjbOptions" :key="index"
  258 + :label="item.fullName" :value="item.id"></el-option>
218 259 </el-select>
219 260 </div>
220 261 <div class="px-staff-field">
221 262 <label>业绩</label>
222   - <el-input v-model="kjb.kjblsyj" placeholder="请输入业绩" size="mini" style="width: 120px;" @input="formatPxStaffNumber(pxIndex, kjbIndex, 'kjblsyj', 'lqHytkKjbsyjList')">
  263 + <el-input v-model="kjb.kjblsyj" placeholder="请输入业绩" size="mini"
  264 + style="width: 120px;"
  265 + @input="formatPxStaffNumber(pxIndex, kjbIndex, 'kjblsyj', 'lqHytkKjbsyjList')">
223 266 <template slot="append">元</template>
224 267 </el-input>
225 268 </div>
226   - <div class="px-staff-field" v-show="false" style="justify-content: space-around;padding-bottom: 30rpx;">
  269 + <div class="px-staff-field" v-show="false"
  270 + style="justify-content: space-around;padding-bottom: 30rpx;">
227 271 <label>手工费</label>
228   - <el-input v-model="kjb.laborCost" placeholder="手工费" size="mini" style="width: 120px;" @input="formatPxStaffNumber(pxIndex, kjbIndex, 'laborCost', 'lqHytkKjbsyjList')">
  272 + <el-input v-model="kjb.laborCost" placeholder="手工费" size="mini"
  273 + style="width: 120px;"
  274 + @input="formatPxStaffNumber(pxIndex, kjbIndex, 'laborCost', 'lqHytkKjbsyjList')">
229 275 <template slot="append">元</template>
230 276 </el-input>
231 277 </div>
232 278 <div class="px-staff-field">
233 279 <label>次数</label>
234   - <el-input-number v-model="kjb.hdpxNumber" :min="1" :max="999" placeholder="次数" size="mini" style="width: 120px;" @change="onKjbNumberChange(pxIndex, kjbIndex, $event)"></el-input-number>
  280 + <el-input-number v-model="kjb.hdpxNumber" :min="1" :max="999"
  281 + placeholder="次数" size="mini" style="width: 120px;"
  282 + @change="onKjbNumberChange(pxIndex, kjbIndex, $event)"></el-input-number>
235 283 </div>
236 284 <div class="px-staff-field" v-if="!dataForm.id">
237   - <el-button size="mini" type="danger" icon="el-icon-delete" @click="removePxKjb(pxIndex, kjbIndex)">删除</el-button>
  285 + <el-button size="mini" type="danger" icon="el-icon-delete"
  286 + @click="removePxKjb(pxIndex, kjbIndex)">删除</el-button>
238 287 </div>
239 288 </div>
240 289 </div>
241 290 </div>
242 291 </div>
243 292 </div>
244   -
  293 +
245 294 <!-- 非科美品项提示 -->
246 295 <div class="px-staff-section" v-if="px.qt2 && px.qt2 !== '科美'">
247 296 <div class="px-staff-header">
... ... @@ -257,18 +306,19 @@
257 306 </el-form-item>
258 307 </el-col>
259 308 <el-col :span="24">
260   - <el-form-item label="备注" prop="bz">
261   - <el-input v-model="dataForm.bz" placeholder="请输入" show-word-limit :style='{"width":"100%"}' type='textarea' :autosize='{"minRows":4,"maxRows":4}' >
262   - </el-input>
263   - </el-form-item>
264   - </el-col>
265   - <el-col :span="24">
266   - <el-form-item label="上传文件" prop="fileUrl">
267   - <NCC-UploadImg v-model="dataForm.fileUrl" :fileSize="30" sizeUnit="MB" :limit="9" >
268   - </NCC-UploadImg>
269   - </el-form-item>
270   - </el-col>
271   - </el-form>
  309 + <el-form-item label="备注" prop="bz">
  310 + <el-input v-model="dataForm.bz" placeholder="请输入" show-word-limit :style='{ "width": "100%" }'
  311 + type='textarea' :autosize='{ "minRows": 4, "maxRows": 4 }'>
  312 + </el-input>
  313 + </el-form-item>
  314 + </el-col>
  315 + <el-col :span="24">
  316 + <el-form-item label="上传文件" prop="fileUrl">
  317 + <NCC-UploadImg v-model="dataForm.fileUrl" :fileSize="30" sizeUnit="MB" :limit="9">
  318 + </NCC-UploadImg>
  319 + </el-form-item>
  320 + </el-col>
  321 + </el-form>
272 322 </el-row>
273 323 <span slot="footer" class="dialog-footer">
274 324 <el-button @click="visible = false">取 消</el-button>
... ... @@ -277,291 +327,291 @@
277 327 </el-dialog>
278 328 </template>
279 329 <script>
280   - import request from '@/utils/request'
281   - import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
282   - import { previewDataInterface } from '@/api/systemData/dataInterface'
283   - import { getInfo } from '@/api/user'
284   - export default {
285   - components: {},
286   - props: [],
287   - data() {
288   - return {
289   - loading: false,
290   - visible: false,
291   - isDetail: false,
292   - dataForm: {
293   - id:'',
294   - id:undefined,
295   - md:undefined,
296   - mdbh:undefined,
297   - mdmc:undefined,
298   - hy:undefined,
299   - hymc:undefined,
300   - hyzh:undefined,
301   - tkje:undefined,
302   - sgfy:undefined,
303   - bz:undefined,
304   - tkpx:undefined,
305   - tksj:new Date(),
306   - tkr:undefined,
307   - fileUrl:undefined,
308   - lqHytkJksyjList:[],
309   - lqHytkKjbsyjList:[],
310   - lqHytkMxList:[],
311   - },
312   - rules: {
313   - md: [
314   - { required: true, message: '请选择门店', trigger: 'change' }
315   - ],
316   - hy: [
317   - { required: true, message: '请选择会员', trigger: 'change' }
318   - ]
319   - },
320   - mdOptions : [],
321   - hyOptions : [],
322   - pxOptions : [],
323   - jksOptions:[],
324   - kjbOptions:[],
  330 +import request from '@/utils/request'
  331 +import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
  332 +import { previewDataInterface } from '@/api/systemData/dataInterface'
  333 +import { getInfo } from '@/api/user'
  334 +export default {
  335 + components: {},
  336 + props: [],
  337 + data() {
  338 + return {
  339 + loading: false,
  340 + visible: false,
  341 + isDetail: false,
  342 + dataForm: {
  343 + id: '',
  344 + id: undefined,
  345 + md: undefined,
  346 + mdbh: undefined,
  347 + mdmc: undefined,
  348 + hy: undefined,
  349 + hymc: undefined,
  350 + hyzh: undefined,
  351 + tkje: undefined,
  352 + sgfy: undefined,
  353 + bz: undefined,
  354 + tkpx: undefined,
  355 + tksj: new Date(),
  356 + tkr: undefined,
  357 + fileUrl: undefined,
  358 + lqHytkJksyjList: [],
  359 + lqHytkKjbsyjList: [],
  360 + lqHytkMxList: [],
  361 + },
  362 + rules: {
  363 + md: [
  364 + { required: true, message: '请选择门店', trigger: 'change' }
  365 + ],
  366 + hy: [
  367 + { required: true, message: '请选择会员', trigger: 'change' }
  368 + ]
  369 + },
  370 + mdOptions: [],
  371 + hyOptions: [],
  372 + pxOptions: [],
  373 + jksOptions: [],
  374 + kjbOptions: [],
  375 + }
  376 + },
  377 + computed: {},
  378 + watch: {},
  379 + created() {
  380 + // 门店数据在loadStoreDataAndSetDefault中加载,避免重复
  381 + this.gethyOptions();
  382 + this.getpxOptions();
  383 + },
  384 + mounted() {
  385 + },
  386 + methods: {
  387 + // 门店选择变化处理
  388 + handleMdChange(val) {
  389 + const selectedMd = this.mdOptions.find(item => item.id === val);
  390 + if (selectedMd) {
  391 + this.dataForm.mdbh = selectedMd.mdbh || selectedMd.id;
  392 + this.dataForm.mdmc = selectedMd.fullName;
  393 + console.log('选择门店:', selectedMd);
  394 + } else {
  395 + this.dataForm.mdbh = '';
  396 + this.dataForm.mdmc = '';
325 397 }
326 398 },
327   - computed: {},
328   - watch: {},
329   - created() {
330   - // 门店数据在loadStoreDataAndSetDefault中加载,避免重复
331   - this.gethyOptions();
332   - this.getpxOptions();
  399 + // 会员选择变化处理
  400 + handleHyChange(val) {
  401 + const selectedHy = this.hyOptions.find(item => item.id === val);
  402 + if (selectedHy) {
  403 + this.dataForm.hyzh = selectedHy.hyzh || selectedHy.id;
  404 + this.dataForm.hymc = selectedHy.fullName;
  405 + console.log('选择会员:', selectedHy);
  406 +
  407 + // 清空已选择的品项列表
  408 + this.dataForm.lqHytkMxList = [];
  409 +
  410 + // 选择会员后,获取该会员的剩余品项
  411 + this.getpxOptions();
  412 + } else {
  413 + this.dataForm.hyzh = '';
  414 + this.dataForm.hymc = '';
  415 + this.pxOptions = []; // 清空品项选项
  416 + this.dataForm.lqHytkMxList = []; // 清空已选择的品项列表
  417 +
  418 + // 重新计算总金额和总手工费
  419 + this.calculateTotalAmount();
  420 + this.calculateTotalLaborCost();
  421 + }
333 422 },
334   - mounted() {
335   - },
336   - methods: {
337   - // 门店选择变化处理
338   - handleMdChange(val) {
339   - const selectedMd = this.mdOptions.find(item => item.id === val);
340   - if (selectedMd) {
341   - this.dataForm.mdbh = selectedMd.mdbh || selectedMd.id;
342   - this.dataForm.mdmc = selectedMd.fullName;
343   - console.log('选择门店:', selectedMd);
344   - } else {
345   - this.dataForm.mdbh = '';
346   - this.dataForm.mdmc = '';
347   - }
348   - },
349   - // 会员选择变化处理
350   - handleHyChange(val) {
351   - const selectedHy = this.hyOptions.find(item => item.id === val);
352   - if (selectedHy) {
353   - this.dataForm.hyzh = selectedHy.hyzh || selectedHy.id;
354   - this.dataForm.hymc = selectedHy.fullName;
355   - console.log('选择会员:', selectedHy);
356   -
357   - // 清空已选择的品项列表
358   - this.dataForm.lqHytkMxList = [];
359   -
360   - // 选择会员后,获取该会员的剩余品项
361   - this.getpxOptions();
362   - } else {
363   - this.dataForm.hyzh = '';
364   - this.dataForm.hymc = '';
365   - this.pxOptions = []; // 清空品项选项
366   - this.dataForm.lqHytkMxList = []; // 清空已选择的品项列表
367   -
368   - // 重新计算总金额和总手工费
369   - this.calculateTotalAmount();
370   - this.calculateTotalLaborCost();
371   - }
372   - },
373   - // 品项选择变化处理
374   - handlePxChange(index, row) {
375   - if (row.pxid) {
376   - const selectedPx = this.pxOptions.find(item => item.pxid === row.pxid);
377   - if (selectedPx) {
378   - request({
379   - url: `/api/Extend/LqXmzl/${selectedPx.id}`,
380   - method: 'GET',
381   - }).then((res) => {
382   - let qt2 = res.data.qt2 || "";
383   - let sgf = res.data.sgf || 0;
384   - row.px = selectedPx.id;
385   - row.pxmc = selectedPx.fullName;
386   - row.pxjg = selectedPx.ItemPrice || 0;
387   - row.qt2 = qt2 || "";
388   - row.yxf = selectedPx.ConsumedCount || 0; // 已消费
389   - row.zgm = selectedPx.TotalPurchased || 1; // 总购买
390   - row.ly = selectedPx.sourceType || "购买"; // 来源
391   - row.projectNumber = 1; // 默认消费数量为1
392   - row.sgf = sgf;
393   -
394   - // 品项切换时,清空所有人员列表
395   - row.lqHytkJksyjList = []; // 清空健康师列表
396   - row.lqHytkKjbsyjList = []; // 清空科技部老师列表
397   -
398   - console.log('选择品项:', selectedPx.fullName, 'qt2:', selectedPx.qt2, '剩余数量:', selectedPx.RemainingCount, '手工费:', sgf);
399   -
400   - // 重新计算总金额和总手工费
401   - this.calculateTotalAmount();
402   - this.calculateTotalLaborCost();
403   - })
404   - }
405   - } else {
406   - // 清空品项时,重置所有相关字段
407   - row.pxmc = '';
408   - row.pxjg = 0;
409   - row.qt2 = '';
410   - row.yxf = 0;
411   - row.zgm = 1;
412   - row.ly = '购买';
413   - row.projectNumber = 1;
414   - row.sgf = 0; // 清空手工费
415   - row.lqHytkJksyjList = []; // 清空健康师列表
416   - row.lqHytkKjbsyjList = []; // 清空科技部老师列表
417   -
418   - // 重新计算总金额和总手工费
419   - this.calculateTotalAmount();
420   - this.calculateTotalLaborCost();
421   - }
422   - },
423   - getmdOptions(){
424   - previewDataInterface('730960205902251269').then(res => {
425   - this.mdOptions = res.data
426   - });
427   - },
428   - gethyOptions(){
429   - previewDataInterface('730998109110273285').then(res => {
430   - this.hyOptions = res.data
431   - });
432   - },
433   - // 获取品项选项(从会员剩余品项API获取)
434   - async getpxOptions() {
435   - if (!this.dataForm.hy) {
436   - console.warn("请先选择会员");
437   - this.pxOptions = [];
438   - return;
  423 + // 品项选择变化处理
  424 + handlePxChange(index, row) {
  425 + if (row.pxid) {
  426 + const selectedPx = this.pxOptions.find(item => item.pxid === row.pxid);
  427 + if (selectedPx) {
  428 + request({
  429 + url: `/api/Extend/LqXmzl/${selectedPx.id}`,
  430 + method: 'GET',
  431 + }).then((res) => {
  432 + let qt2 = res.data.qt2 || "";
  433 + let sgf = res.data.sgf || 0;
  434 + row.px = selectedPx.id;
  435 + row.pxmc = selectedPx.fullName;
  436 + row.pxjg = selectedPx.ItemPrice || 0;
  437 + row.qt2 = qt2 || "";
  438 + row.yxf = selectedPx.ConsumedCount || 0; // 已消费
  439 + row.zgm = selectedPx.TotalPurchased || 1; // 总购买
  440 + row.ly = selectedPx.sourceType || "购买"; // 来源
  441 + row.projectNumber = 1; // 默认消费数量为1
  442 + row.sgf = sgf;
  443 +
  444 + // 品项切换时,清空所有人员列表
  445 + row.lqHytkJksyjList = []; // 清空健康师列表
  446 + row.lqHytkKjbsyjList = []; // 清空科技部老师列表
  447 +
  448 + console.log('选择品项:', selectedPx.fullName, 'qt2:', selectedPx.qt2, '剩余数量:', selectedPx.RemainingCount, '手工费:', sgf);
  449 +
  450 + // 重新计算总金额和总手工费
  451 + this.calculateTotalAmount();
  452 + this.calculateTotalLaborCost();
  453 + })
439 454 }
  455 + } else {
  456 + // 清空品项时,重置所有相关字段
  457 + row.pxmc = '';
  458 + row.pxjg = 0;
  459 + row.qt2 = '';
  460 + row.yxf = 0;
  461 + row.zgm = 1;
  462 + row.ly = '购买';
  463 + row.projectNumber = 1;
  464 + row.sgf = 0; // 清空手工费
  465 + row.lqHytkJksyjList = []; // 清空健康师列表
  466 + row.lqHytkKjbsyjList = []; // 清空科技部老师列表
440 467  
441   - try {
442   - const response = await request({
443   - url: `/api/Extend/lqkhxx/GetMemberRemainingItems?memberId=${this.dataForm.hy}`,
444   - method: 'GET',
445   - });
  468 + // 重新计算总金额和总手工费
  469 + this.calculateTotalAmount();
  470 + this.calculateTotalLaborCost();
  471 + }
  472 + },
  473 + getmdOptions() {
  474 + previewDataInterface('730960205902251269').then(res => {
  475 + this.mdOptions = res.data
  476 + });
  477 + },
  478 + gethyOptions() {
  479 + previewDataInterface('730998109110273285').then(res => {
  480 + this.hyOptions = res.data
  481 + });
  482 + },
  483 + // 获取品项选项(从会员剩余品项API获取)
  484 + async getpxOptions() {
  485 + if (!this.dataForm.hy) {
  486 + console.warn("请先选择会员");
  487 + this.pxOptions = [];
  488 + return;
  489 + }
446 490  
447   - if (response.code == 200 && response.data && response.data.RemainingItems) {
448   - this.pxOptions = response.data.RemainingItems.map(item => ({
449   - fullName: item.ItemName,
450   - id: item.ItemId,
451   - value: item.ItemId,
452   - label: item.ItemName,
453   - px: item.ItemId,
454   - pxmc: item.ItemName,
455   - pxjg: item.ItemPrice || 0,
456   - qt2: item.qt2 || "",
457   - RemainingCount: item.RemainingCount || 0,
458   - ItemPrice: item.ItemPrice || 0,
459   - sgf: 0,
460   - sourceType: item.SourceType || "购买",
461   - TotalPurchased: item.TotalPurchased || 0,
462   - ConsumedCount: item.ConsumedCount || 0,
463   - pxid: item.ItemId+','+item.SourceType
464   - }));
465   - console.log('获取会员剩余品项成功:', this.pxOptions);
466   - } else {
467   - console.warn("获取会员剩余品项失败,使用默认数据");
468   - this.pxOptions = [];
469   - }
470   - } catch (error) {
471   - console.error("获取会员剩余品项出错:", error);
472   - this.pxOptions = [];
473   - }
474   - },
475   - // 获取健康师选项
476   - getjksOptions() {
477   - request({
478   - url: `/api/Extend/user?page=1&pageSize=1000&mdid=${this.dataForm.md}&gw=健康师`,
479   - method: 'GET',
480   - }).then((res) => {
481   - if (res.code == 200 && res.data.list.length > 0) {
482   - this.jksOptions = res.data.list.map(item => ({
483   - fullName: item.realName || item.name || item.userName,
484   - id: item.id,
485   - value: item.id,
486   - label: item.realName || item.name || item.userName,
487   - jks: item.id,
488   - jksxm: item.realName || item.name || item.userName,
489   - jkszh: item.mobilePhone || item.account || item.userName
490   - }));
491   - } else {
492   - this.jksOptions = [];
493   - }
494   - })
495   - },
496   - // 获取科技部老师选项
497   - async getkjbOptions() {
498   - await this.getKjbOptionsByOrganizeId();
499   - },
500   - // 根据门店ID获取科技部组织ID,再获取科技部老师列表
501   - async getKjbOptionsByOrganizeId() {
502   - const userResponse = await request({
503   - url: `/api/Extend/user?page=1&pageSize=1000&gw=科技老师`,
  491 + try {
  492 + const response = await request({
  493 + url: `/api/Extend/lqkhxx/GetMemberRemainingItems?memberId=${this.dataForm.hy}`,
504 494 method: 'GET',
505 495 });
506   -
507   - if (userResponse.code == 200 && userResponse.data && userResponse.data.list.length > 0) {
508   - this.kjbOptions = userResponse.data.list.map(item => ({
  496 +
  497 + if (response.code == 200 && response.data && response.data.RemainingItems) {
  498 + this.pxOptions = response.data.RemainingItems.map(item => ({
  499 + fullName: item.ItemName,
  500 + id: item.ItemId,
  501 + value: item.ItemId,
  502 + label: item.ItemName,
  503 + px: item.ItemId,
  504 + pxmc: item.ItemName,
  505 + pxjg: item.ItemPrice || 0,
  506 + qt2: item.qt2 || "",
  507 + RemainingCount: item.RemainingCount || 0,
  508 + ItemPrice: item.ItemPrice || 0,
  509 + sgf: 0,
  510 + sourceType: item.SourceType || "购买",
  511 + TotalPurchased: item.TotalPurchased || 0,
  512 + ConsumedCount: item.ConsumedCount || 0,
  513 + pxid: item.ItemId + ',' + item.SourceType
  514 + }));
  515 + console.log('获取会员剩余品项成功:', this.pxOptions);
  516 + } else {
  517 + console.warn("获取会员剩余品项失败,使用默认数据");
  518 + this.pxOptions = [];
  519 + }
  520 + } catch (error) {
  521 + console.error("获取会员剩余品项出错:", error);
  522 + this.pxOptions = [];
  523 + }
  524 + },
  525 + // 获取健康师选项
  526 + getjksOptions() {
  527 + request({
  528 + url: `/api/Extend/user?page=1&pageSize=1000&mdid=${this.dataForm.md}&gw=健康师`,
  529 + method: 'GET',
  530 + }).then((res) => {
  531 + if (res.code == 200 && res.data.list.length > 0) {
  532 + this.jksOptions = res.data.list.map(item => ({
509 533 fullName: item.realName || item.name || item.userName,
510 534 id: item.id,
511 535 value: item.id,
512 536 label: item.realName || item.name || item.userName,
513   - kjbls: item.id,
514   - kjblsxm: item.realName || item.name || item.userName,
515   - kjblszh: item.mobilePhone || item.account || item.userName
  537 + jks: item.id,
  538 + jksxm: item.realName || item.name || item.userName,
  539 + jkszh: item.mobilePhone || item.account || item.userName
516 540 }));
517   - console.log('科技部老师列表:', this.kjbOptions);
518 541 } else {
519   - console.warn('获取科技部老师列表失败');
520   - this.kjbOptions = [];
  542 + this.jksOptions = [];
521 543 }
522   - },
523   - // 品项健康师选择
524   - handleJksChange(pxIndex, jksIndex, row) {
525   - console.log('品项健康师选择:', row);
526   - if (row.jks) {
527   - // 根据选择的健康师ID获取用户信息
528   - const selectedJks = this.jksOptions.find(item => item.id === row.jks);
529   - if (selectedJks) {
530   - // 填充姓名和账号
531   - row.jksxm = selectedJks.fullName;
532   - row.jkszh = selectedJks.userName || selectedJks.account || selectedJks.id;
533   -
534   - // 获取金三角信息
535   - this.getJsjInfoByUserId(row.jks, (jsjId, jsjName) => {
536   - row.jsjName = jsjName;
537   - row.jsjId = jsjId;
538   - });
539   - }
540   - } else {
541   - // 清空相关字段
542   - row.jksxm = '';
543   - row.jkszh = '';
544   - row.jsjId = '';
  544 + })
  545 + },
  546 + // 获取科技部老师选项
  547 + async getkjbOptions() {
  548 + await this.getKjbOptionsByOrganizeId();
  549 + },
  550 + // 根据门店ID获取科技部组织ID,再获取科技部老师列表
  551 + async getKjbOptionsByOrganizeId() {
  552 + const userResponse = await request({
  553 + url: `/api/Extend/user?page=1&pageSize=1000&gw=科技老师`,
  554 + method: 'GET',
  555 + });
  556 +
  557 + if (userResponse.code == 200 && userResponse.data && userResponse.data.list.length > 0) {
  558 + this.kjbOptions = userResponse.data.list.map(item => ({
  559 + fullName: item.realName || item.name || item.userName,
  560 + id: item.id,
  561 + value: item.id,
  562 + label: item.realName || item.name || item.userName,
  563 + kjbls: item.id,
  564 + kjblsxm: item.realName || item.name || item.userName,
  565 + kjblszh: item.mobilePhone || item.account || item.userName
  566 + }));
  567 + console.log('科技部老师列表:', this.kjbOptions);
  568 + } else {
  569 + console.warn('获取科技部老师列表失败');
  570 + this.kjbOptions = [];
  571 + }
  572 + },
  573 + // 品项健康师选择
  574 + handleJksChange(pxIndex, jksIndex, row) {
  575 + console.log('品项健康师选择:', row);
  576 + if (row.jks) {
  577 + // 根据选择的健康师ID获取用户信息
  578 + const selectedJks = this.jksOptions.find(item => item.id === row.jks);
  579 + if (selectedJks) {
  580 + // 填充姓名和账号
  581 + row.jksxm = selectedJks.fullName;
  582 + row.jkszh = selectedJks.userName || selectedJks.account || selectedJks.id;
  583 +
  584 + // 获取金三角信息
  585 + this.getJsjInfoByUserId(row.jks, (jsjId, jsjName) => {
  586 + row.jsjName = jsjName;
  587 + row.jsjId = jsjId;
  588 + });
545 589 }
546   - },
547   - // 品项科技部老师选择
548   - handleKjbChange(pxIndex, kjbIndex, row) {
549   - console.log('品项科技部老师选择:', row);
550   - if (row.kjbls) {
551   - // 根据选择的科技部老师ID获取用户信息
552   - const selectedKjb = this.kjbOptions.find(item => item.id === row.kjbls);
553   - if (selectedKjb) {
554   - // 填充姓名和账号
555   - row.kjblsxm = selectedKjb.fullName;
556   - row.kjblszh = selectedKjb.userName || selectedKjb.account || selectedKjb.id;
557   - }
558   - } else {
559   - // 清空相关字段
560   - row.kjblsxm = '';
561   - row.kjblszh = '';
  590 + } else {
  591 + // 清空相关字段
  592 + row.jksxm = '';
  593 + row.jkszh = '';
  594 + row.jsjId = '';
  595 + }
  596 + },
  597 + // 品项科技部老师选择
  598 + handleKjbChange(pxIndex, kjbIndex, row) {
  599 + console.log('品项科技部老师选择:', row);
  600 + if (row.kjbls) {
  601 + // 根据选择的科技部老师ID获取用户信息
  602 + const selectedKjb = this.kjbOptions.find(item => item.id === row.kjbls);
  603 + if (selectedKjb) {
  604 + // 填充姓名和账号
  605 + row.kjblsxm = selectedKjb.fullName;
  606 + row.kjblszh = selectedKjb.userName || selectedKjb.account || selectedKjb.id;
562 607 }
563   - },
564   - // 格式化日期方法
  608 + } else {
  609 + // 清空相关字段
  610 + row.kjblsxm = '';
  611 + row.kjblszh = '';
  612 + }
  613 + },
  614 + // 格式化日期方法
565 615 formatDate(date, format) {
566 616 if (!date) return '';
567 617 const d = new Date(date);
... ... @@ -571,7 +621,7 @@
571 621 const hours = String(d.getHours()).padStart(2, '0');
572 622 const minutes = String(d.getMinutes()).padStart(2, '0');
573 623 const seconds = String(d.getSeconds()).padStart(2, '0');
574   -
  624 +
575 625 return format
576 626 .replace('yyyy', year)
577 627 .replace('MM', month)
... ... @@ -580,629 +630,629 @@
580 630 .replace('mm', minutes)
581 631 .replace('ss', seconds);
582 632 },
583   - // 根据用户ID获取金三角信息
584   - getJsjInfoByUserId(userId, callback) {
585   - let date = new Date(this.dataForm.tksj);
586   - let formattedDate = this.formatDate(date, 'yyyy-MM-dd HH:mm:ss');
587   - console.log('formattedDate:', formattedDate);
588   - request({
589   - url: `/api/Extend/lqycsdjsj/GetJsjInfoByUserMonth?UserId=${userId}&DateTime=${formattedDate}`,
590   - method: 'GET',
591   - }).then((res) => {
592   - if (res.code === 200 && res.data) {
593   - // 假设返回的数据结构中有jsj_id字段
594   - const jsjId = res.data.jsjId;
595   - const jsjName = res.data.jsjName;
596   -
597   - if (callback) {
598   - callback(jsjId, jsjName);
599   - }
600   - console.log('获取金三角信息成功:', res.data);
601   - } else {
602   - console.warn('获取金三角信息失败:', res.msg);
603   - if (callback) {
604   - callback('');
605   - }
  633 + // 根据用户ID获取金三角信息
  634 + getJsjInfoByUserId(userId, callback) {
  635 + let date = new Date(this.dataForm.tksj);
  636 + let formattedDate = this.formatDate(date, 'yyyy-MM-dd HH:mm:ss');
  637 + console.log('formattedDate:', formattedDate);
  638 + request({
  639 + url: `/api/Extend/lqycsdjsj/GetJsjInfoByUserMonth?UserId=${userId}&DateTime=${formattedDate}`,
  640 + method: 'GET',
  641 + }).then((res) => {
  642 + if (res.code === 200 && res.data) {
  643 + // 假设返回的数据结构中有jsj_id字段
  644 + const jsjId = res.data.jsjId;
  645 + const jsjName = res.data.jsjName;
  646 +
  647 + if (callback) {
  648 + callback(jsjId, jsjName);
606 649 }
607   - }).catch((err) => {
608   - console.error('获取金三角信息出错:', err);
  650 + console.log('获取金三角信息成功:', res.data);
  651 + } else {
  652 + console.warn('获取金三角信息失败:', res.msg);
609 653 if (callback) {
610 654 callback('');
611 655 }
612   - });
613   - },
614   - // 品项内人员业绩数字格式化
615   - formatPxStaffNumber(pxIndex, staffIndex, field, listName) {
616   - const targetList = this.dataForm.lqHytkMxList[pxIndex][listName];
617   - if (targetList && targetList[staffIndex] && targetList[staffIndex][field] !== undefined) {
618   - let value = targetList[staffIndex][field].toString().replace(/,/g, '');
619   -
620   - if (!/^\d*\.?\d*$/.test(value)) {
621   - value = value.replace(/[^\d.]/g, '');
622   - }
623   -
624   - targetList[staffIndex][field] = value;
625 656 }
626   - },
627   - // 获取品项剩余数量
628   - getRemainingCount(px) {
629   - if (px.px) {
630   - const selectedPx = this.pxOptions.find(item => item.id === px.px);
631   - if (selectedPx) {
632   - return selectedPx.RemainingCount || 0;
633   - }
  657 + }).catch((err) => {
  658 + console.error('获取金三角信息出错:', err);
  659 + if (callback) {
  660 + callback('');
634 661 }
635   - return (px.zgm || 1) - (px.yxf || 0);
636   - },
637   - // 消费数量变化处理
638   - onProjectNumberChange() {
639   - // 重新计算总金额和总手工费
640   - this.calculateTotalAmount();
641   - this.calculateTotalLaborCost();
642   - },
643   - // 品项消费数量变化处理(带参数)
644   - onPxProjectNumberChange(pxIndex, projectNumber) {
645   - // 更新品项的消费数量
646   - this.dataForm.lqHytkMxList[pxIndex].projectNumber = projectNumber;
647   -
648   - // 获取当前品项的手工费
649   - const currentPx = this.dataForm.lqHytkMxList[pxIndex];
650   - const laborCost = currentPx.sgf || 0;
651   -
652   - // 更新该品项下所有人员的手工费
653   - this.updatePxStaffLaborCost(pxIndex, laborCost);
654   -
655   - // 重新计算总金额和总手工费
656   - this.calculateTotalAmount();
657   - this.calculateTotalLaborCost();
658   - },
659   - // 更新品项下所有人员的手工费
660   - updatePxStaffLaborCost(pxIndex, newLaborCost) {
661   - const currentPx = this.dataForm.lqHytkMxList[pxIndex];
662   - console.log('更新品项手工费:', {
663   - pxIndex,
664   - newLaborCost,
665   - pxName: currentPx.pxmc,
666   - hasJks: currentPx.lqHytkJksyjList && currentPx.lqHytkJksyjList.length > 0,
667   - hasKjb: currentPx.lqHytkKjbsyjList && currentPx.lqHytkKjbsyjList.length > 0
668   - });
669   -
670   - // 更新健康师的手工费 = 品项手工费 × 健康师次数
671   - if (currentPx.lqHytkJksyjList && currentPx.lqHytkJksyjList.length > 0) {
672   - currentPx.lqHytkJksyjList.forEach((jks, index) => {
673   - const jksLaborCost = newLaborCost * (jks.kdpxNumber || 1);
674   - console.log(`更新健康师${index}手工费:`, jks.laborCost, '->', jksLaborCost, `(${newLaborCost} × ${jks.kdpxNumber || 1})`);
675   - // 使用Vue.set确保响应式更新
676   - this.$set(jks, 'laborCost', jksLaborCost);
677   - });
  662 + });
  663 + },
  664 + // 品项内人员业绩数字格式化
  665 + formatPxStaffNumber(pxIndex, staffIndex, field, listName) {
  666 + const targetList = this.dataForm.lqHytkMxList[pxIndex][listName];
  667 + if (targetList && targetList[staffIndex] && targetList[staffIndex][field] !== undefined) {
  668 + let value = targetList[staffIndex][field].toString().replace(/,/g, '');
  669 +
  670 + if (!/^\d*\.?\d*$/.test(value)) {
  671 + value = value.replace(/[^\d.]/g, '');
678 672 }
679   -
680   - // 更新科技部老师的手工费 = 品项手工费 × 科技部老师次数
681   - if (currentPx.lqHytkKjbsyjList && currentPx.lqHytkKjbsyjList.length > 0) {
682   - currentPx.lqHytkKjbsyjList.forEach((kjb, index) => {
683   - const kjbLaborCost = newLaborCost * (kjb.hdpxNumber || 1);
684   - console.log(`更新科技部老师${index}手工费:`, kjb.laborCost, '->', kjbLaborCost, `(${newLaborCost} × ${kjb.hdpxNumber || 1})`);
685   - // 使用Vue.set确保响应式更新
686   - this.$set(kjb, 'laborCost', kjbLaborCost);
687   - });
  673 +
  674 + targetList[staffIndex][field] = value;
  675 + }
  676 + },
  677 + // 获取品项剩余数量
  678 + getRemainingCount(px) {
  679 + if (px.px) {
  680 + const selectedPx = this.pxOptions.find(item => item.id === px.px);
  681 + if (selectedPx) {
  682 + return selectedPx.RemainingCount || 0;
688 683 }
689   - },
690   - // 健康师次数变化处理
691   - onJksNumberChange(pxIndex, jksIndex, newNumber) {
692   - const currentPx = this.dataForm.lqHytkMxList[pxIndex];
693   - const jks = currentPx.lqHytkJksyjList[jksIndex];
694   -
695   - // 更新健康师的次数
696   - this.$set(jks, 'kdpxNumber', newNumber);
697   -
698   - // 重新计算该健康师的手工费 = 品项手工费 × 健康师次数
699   - const pxLaborCost = currentPx.sgf || 0;
700   - const newJksLaborCost = pxLaborCost * newNumber;
701   - this.$set(jks, 'laborCost', newJksLaborCost);
702   -
703   - // 重新计算总金额和总手工费
704   - this.calculateTotalAmount();
705   - this.calculateTotalLaborCost();
706   -
707   - console.log('健康师次数变化:', {
708   - pxIndex,
709   - jksIndex,
710   - newNumber,
711   - jksName: jks.jksxm,
712   - pxLaborCost,
713   - newJksLaborCost,
714   - formula: `${pxLaborCost} × ${newNumber} = ${newJksLaborCost}`
715   - });
716   - },
717   - // 科技部老师次数变化处理
718   - onKjbNumberChange(pxIndex, kjbIndex, newNumber) {
719   - const currentPx = this.dataForm.lqHytkMxList[pxIndex];
720   - const kjb = currentPx.lqHytkKjbsyjList[kjbIndex];
721   -
722   - // 更新科技部老师的次数
723   - this.$set(kjb, 'hdpxNumber', newNumber);
724   -
725   - // 重新计算该科技部老师的手工费 = 品项手工费 × 科技部老师次数
726   - const pxLaborCost = currentPx.sgf || 0;
727   - const newKjbLaborCost = pxLaborCost * newNumber;
728   - this.$set(kjb, 'laborCost', newKjbLaborCost);
729   -
730   - // 重新计算总金额和总手工费
731   - this.calculateTotalAmount();
732   - this.calculateTotalLaborCost();
733   -
734   - console.log('科技部老师次数变化:', {
735   - pxIndex,
736   - kjbIndex,
737   - newNumber,
738   - kjbName: kjb.kjblsxm,
739   - pxLaborCost,
740   - newKjbLaborCost,
741   - formula: `${pxLaborCost} × ${newNumber} = ${newKjbLaborCost}`
  684 + }
  685 + return (px.zgm || 1) - (px.yxf || 0);
  686 + },
  687 + // 消费数量变化处理
  688 + onProjectNumberChange() {
  689 + // 重新计算总金额和总手工费
  690 + this.calculateTotalAmount();
  691 + this.calculateTotalLaborCost();
  692 + },
  693 + // 品项消费数量变化处理(带参数)
  694 + onPxProjectNumberChange(pxIndex, projectNumber) {
  695 + // 更新品项的消费数量
  696 + this.dataForm.lqHytkMxList[pxIndex].projectNumber = projectNumber;
  697 +
  698 + // 获取当前品项的手工费
  699 + const currentPx = this.dataForm.lqHytkMxList[pxIndex];
  700 + const laborCost = currentPx.sgf || 0;
  701 +
  702 + // 更新该品项下所有人员的手工费
  703 + this.updatePxStaffLaborCost(pxIndex, laborCost);
  704 +
  705 + // 重新计算总金额和总手工费
  706 + this.calculateTotalAmount();
  707 + this.calculateTotalLaborCost();
  708 + },
  709 + // 更新品项下所有人员的手工费
  710 + updatePxStaffLaborCost(pxIndex, newLaborCost) {
  711 + const currentPx = this.dataForm.lqHytkMxList[pxIndex];
  712 + console.log('更新品项手工费:', {
  713 + pxIndex,
  714 + newLaborCost,
  715 + pxName: currentPx.pxmc,
  716 + hasJks: currentPx.lqHytkJksyjList && currentPx.lqHytkJksyjList.length > 0,
  717 + hasKjb: currentPx.lqHytkKjbsyjList && currentPx.lqHytkKjbsyjList.length > 0
  718 + });
  719 +
  720 + // 更新健康师的手工费 = 品项手工费 × 健康师次数
  721 + if (currentPx.lqHytkJksyjList && currentPx.lqHytkJksyjList.length > 0) {
  722 + currentPx.lqHytkJksyjList.forEach((jks, index) => {
  723 + const jksLaborCost = newLaborCost * (jks.kdpxNumber || 1);
  724 + console.log(`更新健康师${index}手工费:`, jks.laborCost, '->', jksLaborCost, `(${newLaborCost} × ${jks.kdpxNumber || 1})`);
  725 + // 使用Vue.set确保响应式更新
  726 + this.$set(jks, 'laborCost', jksLaborCost);
742 727 });
743   - },
744   - // 计算总退款金额
745   - calculateTotalAmount() {
746   - let totalAmount = 0;
747   - this.dataForm.lqHytkMxList.forEach(px => {
748   - if (px.px && px.pxjg && px.projectNumber) {
749   - totalAmount += parseFloat(px.pxjg) * parseInt(px.projectNumber);
750   - }
  728 + }
  729 +
  730 + // 更新科技部老师的手工费 = 品项手工费 × 科技部老师次数
  731 + if (currentPx.lqHytkKjbsyjList && currentPx.lqHytkKjbsyjList.length > 0) {
  732 + currentPx.lqHytkKjbsyjList.forEach((kjb, index) => {
  733 + const kjbLaborCost = newLaborCost * (kjb.hdpxNumber || 1);
  734 + console.log(`更新科技部老师${index}手工费:`, kjb.laborCost, '->', kjbLaborCost, `(${newLaborCost} × ${kjb.hdpxNumber || 1})`);
  735 + // 使用Vue.set确保响应式更新
  736 + this.$set(kjb, 'laborCost', kjbLaborCost);
751 737 });
752   - this.dataForm.tkje = totalAmount.toFixed(2);
753   - },
754   - // 计算总手工费
755   - calculateTotalLaborCost() {
756   - let totalLaborCost = 0;
757   - this.dataForm.lqHytkMxList.forEach(px => {
758   - // 只计算品项的手工费 = 品项单次手工费 × 品项消费次数
759   - if (px.sgf && px.projectNumber) {
760   - totalLaborCost += parseFloat(px.sgf) * parseInt(px.projectNumber);
  738 + }
  739 + },
  740 + // 健康师次数变化处理
  741 + onJksNumberChange(pxIndex, jksIndex, newNumber) {
  742 + const currentPx = this.dataForm.lqHytkMxList[pxIndex];
  743 + const jks = currentPx.lqHytkJksyjList[jksIndex];
  744 +
  745 + // 更新健康师的次数
  746 + this.$set(jks, 'kdpxNumber', newNumber);
  747 +
  748 + // 重新计算该健康师的手工费 = 品项手工费 × 健康师次数
  749 + const pxLaborCost = currentPx.sgf || 0;
  750 + const newJksLaborCost = pxLaborCost * newNumber;
  751 + this.$set(jks, 'laborCost', newJksLaborCost);
  752 +
  753 + // 重新计算总金额和总手工费
  754 + this.calculateTotalAmount();
  755 + this.calculateTotalLaborCost();
  756 +
  757 + console.log('健康师次数变化:', {
  758 + pxIndex,
  759 + jksIndex,
  760 + newNumber,
  761 + jksName: jks.jksxm,
  762 + pxLaborCost,
  763 + newJksLaborCost,
  764 + formula: `${pxLaborCost} × ${newNumber} = ${newJksLaborCost}`
  765 + });
  766 + },
  767 + // 科技部老师次数变化处理
  768 + onKjbNumberChange(pxIndex, kjbIndex, newNumber) {
  769 + const currentPx = this.dataForm.lqHytkMxList[pxIndex];
  770 + const kjb = currentPx.lqHytkKjbsyjList[kjbIndex];
  771 +
  772 + // 更新科技部老师的次数
  773 + this.$set(kjb, 'hdpxNumber', newNumber);
  774 +
  775 + // 重新计算该科技部老师的手工费 = 品项手工费 × 科技部老师次数
  776 + const pxLaborCost = currentPx.sgf || 0;
  777 + const newKjbLaborCost = pxLaborCost * newNumber;
  778 + this.$set(kjb, 'laborCost', newKjbLaborCost);
  779 +
  780 + // 重新计算总金额和总手工费
  781 + this.calculateTotalAmount();
  782 + this.calculateTotalLaborCost();
  783 +
  784 + console.log('科技部老师次数变化:', {
  785 + pxIndex,
  786 + kjbIndex,
  787 + newNumber,
  788 + kjbName: kjb.kjblsxm,
  789 + pxLaborCost,
  790 + newKjbLaborCost,
  791 + formula: `${pxLaborCost} × ${newNumber} = ${newKjbLaborCost}`
  792 + });
  793 + },
  794 + // 计算总退款金额
  795 + calculateTotalAmount() {
  796 + let totalAmount = 0;
  797 + this.dataForm.lqHytkMxList.forEach(px => {
  798 + if (px.px && px.pxjg && px.projectNumber) {
  799 + totalAmount += parseFloat(px.pxjg) * parseInt(px.projectNumber);
  800 + }
  801 + });
  802 + this.dataForm.tkje = totalAmount.toFixed(2);
  803 + },
  804 + // 计算总手工费
  805 + calculateTotalLaborCost() {
  806 + let totalLaborCost = 0;
  807 + this.dataForm.lqHytkMxList.forEach(px => {
  808 + // 只计算品项的手工费 = 品项单次手工费 × 品项消费次数
  809 + if (px.sgf && px.projectNumber) {
  810 + totalLaborCost += parseFloat(px.sgf) * parseInt(px.projectNumber);
  811 + }
  812 + });
  813 + this.dataForm.sgfy = totalLaborCost.toFixed(2);
  814 + console.log('计算总手工费(仅品项):', totalLaborCost, '各品项手工费:', this.dataForm.lqHytkMxList.map(px => ({
  815 + name: px.pxmc,
  816 + sgf: px.sgf,
  817 + projectNumber: px.projectNumber,
  818 + total: parseFloat(px.sgf || 0) * parseInt(px.projectNumber || 0)
  819 + })));
  820 + },
  821 + goBack() {
  822 + this.$emit('refresh')
  823 + },
  824 + init(id, isDetail) {
  825 + this.dataForm.id = id || 0;
  826 + this.visible = true;
  827 + this.isDetail = isDetail || false;
  828 + this.$nextTick(() => {
  829 + this.$refs['elForm'].resetFields();
  830 + if (this.dataForm.id) {
  831 + request({
  832 + url: '/api/Extend/LqHytkHytk/' + this.dataForm.id,
  833 + method: 'get'
  834 + }).then(res => {
  835 + this.dataForm = res.data;
  836 + this.dataForm.fileUrl = this.dataForm.fileUrl ? JSON.parse(this.dataForm.fileUrl) : [];
  837 + })
  838 + } else {
  839 + // 新建时自动设置当前用户门店
  840 + this.loadStoreDataAndSetDefault();
  841 + // 加载健康师和科技部老师选项
  842 + this.getjksOptions();
  843 + this.getkjbOptions();
  844 + // 初始化时计算总金额和总手工费
  845 + this.calculateTotalAmount();
  846 + this.calculateTotalLaborCost();
  847 + }
  848 + })
  849 + },
  850 + dataFormSubmit() {
  851 +
  852 + this.$refs['elForm'].validate((valid) => {
  853 + if (valid) {
  854 + // 验证品项数据
  855 + if (!this.validatePxList()) {
  856 + return;
761 857 }
762   - });
763   - this.dataForm.sgfy = totalLaborCost.toFixed(2);
764   - console.log('计算总手工费(仅品项):', totalLaborCost, '各品项手工费:', this.dataForm.lqHytkMxList.map(px => ({
765   - name: px.pxmc,
766   - sgf: px.sgf,
767   - projectNumber: px.projectNumber,
768   - total: parseFloat(px.sgf || 0) * parseInt(px.projectNumber || 0)
769   - })));
770   - },
771   - goBack() {
772   - this.$emit('refresh')
773   - },
774   - init(id, isDetail) {
775   - this.dataForm.id = id || 0;
776   - this.visible = true;
777   - this.isDetail = isDetail || false;
778   - this.$nextTick(() => {
779   - this.$refs['elForm'].resetFields();
780   - if (this.dataForm.id) {
  858 +
  859 + // 处理提交格式
  860 + this.processFormData();
  861 + console.log(this.dataForm)
  862 + // return
  863 +
  864 + this.dataForm.fileUrl = JSON.stringify(this.dataForm.fileUrl);
  865 + if (!this.dataForm.id) {
781 866 request({
782   - url: '/api/Extend/LqHytkHytk/' + this.dataForm.id,
783   - method: 'get'
784   - }).then(res =>{
785   - this.dataForm = res.data;
786   - this.dataForm.fileUrl = this.dataForm.fileUrl?JSON.parse(this.dataForm.fileUrl):[];
  867 + url: `/api/Extend/LqHytkHytk`,
  868 + method: 'post',
  869 + data: this.dataForm,
  870 + }).then((res) => {
  871 + this.$message({
  872 + message: res.msg,
  873 + type: 'success',
  874 + duration: 1000,
  875 + onClose: () => {
  876 + this.visible = false,
  877 + this.$emit('refresh', true)
  878 + }
  879 + })
787 880 })
788 881 } else {
789   - // 新建时自动设置当前用户门店
790   - this.loadStoreDataAndSetDefault();
791   - // 加载健康师和科技部老师选项
792   - this.getjksOptions();
793   - this.getkjbOptions();
794   - // 初始化时计算总金额和总手工费
795   - this.calculateTotalAmount();
796   - this.calculateTotalLaborCost();
  882 + request({
  883 + url: '/api/Extend/LqHytkHytk/' + this.dataForm.id,
  884 + method: 'PUT',
  885 + data: this.dataForm
  886 + }).then((res) => {
  887 + this.$message({
  888 + message: res.msg,
  889 + type: 'success',
  890 + duration: 1000,
  891 + onClose: () => {
  892 + this.visible = false
  893 + this.$emit('refresh', true)
  894 + }
  895 + })
  896 + })
797 897 }
798   - })
799   - },
800   - dataFormSubmit() {
801   -
802   - this.$refs['elForm'].validate((valid) => {
803   - if (valid) {
804   - // 验证品项数据
805   - if (!this.validatePxList()) {
806   - return;
807   - }
  898 + }
  899 + })
  900 + },
  901 + // 品项相关方法
  902 + addHandleLqXhPxmxEntityList() {
  903 + let item = {
  904 + id: undefined,
  905 + glkdbh: undefined,
  906 + px: undefined,
  907 + pxmc: undefined,
  908 + pxjg: undefined,
  909 + xfzs: undefined,
  910 + yxf: 0, // 已消费
  911 + zgm: 1, // 总购买
  912 + ly: '购买', // 来源
  913 + projectNumber: 1, // 消费数量
  914 + qt2: undefined, // 品项类型
  915 + lqHytkJksyjList: [], // 健康师业绩列表
  916 + lqHytkKjbsyjList: [], // 科技部老师业绩列表
  917 + }
  918 + this.dataForm.lqHytkMxList.push(item);
  919 + // 重新计算总金额和总手工费
  920 + this.calculateTotalAmount();
  921 + this.calculateTotalLaborCost();
  922 + },
  923 + handleDelLqXhPxmxEntityList(index) {
  924 + this.dataForm.lqHytkMxList.splice(index, 1);
  925 + // 重新计算总金额和总手工费
  926 + this.calculateTotalAmount();
  927 + this.calculateTotalLaborCost();
  928 + },
  929 + validatePxList() {
  930 + const pxList = this.dataForm.lqHytkMxList;
808 931  
809   - // 处理提交格式
810   - this.processFormData();
811   - console.log(this.dataForm)
812   - // return
813   -
814   - this.dataForm.fileUrl = JSON.stringify(this.dataForm.fileUrl);
815   - if (!this.dataForm.id) {
816   - request({
817   - url: `/api/Extend/LqHytkHytk`,
818   - method: 'post',
819   - data: this.dataForm,
820   - }).then((res) => {
821   - this.$message({
822   - message: res.msg,
823   - type: 'success',
824   - duration: 1000,
825   - onClose: () => {
826   - this.visible = false,
827   - this.$emit('refresh', true)
828   - }
829   - })
830   - })
831   - } else {
832   - request({
833   - url: '/api/Extend/LqHytkHytk/' + this.dataForm.id,
834   - method: 'PUT',
835   - data: this.dataForm
836   - }).then((res) => {
837   - this.$message({
838   - message: res.msg,
839   - type: 'success',
840   - duration: 1000,
841   - onClose: () => {
842   - this.visible = false
843   - this.$emit('refresh', true)
844   - }
845   - })
846   - })
847   - }
848   - }
849   - })
850   - },
851   - // 品项相关方法
852   - addHandleLqXhPxmxEntityList() {
853   - let item = {
854   - id: undefined,
855   - glkdbh: undefined,
856   - px: undefined,
857   - pxmc: undefined,
858   - pxjg: undefined,
859   - xfzs: undefined,
860   - yxf: 0, // 已消费
861   - zgm: 1, // 总购买
862   - ly: '购买', // 来源
863   - projectNumber: 1, // 消费数量
864   - qt2: undefined, // 品项类型
865   - lqHytkJksyjList: [], // 健康师业绩列表
866   - lqHytkKjbsyjList: [], // 科技部老师业绩列表
  932 + // 1. 验证是否有品项
  933 + if (!pxList || pxList.length === 0) {
  934 + this.$message.error('请至少添加一个品项');
  935 + return false;
  936 + }
  937 +
  938 + // 2. 验证每个品项的信息
  939 + for (let i = 0; i < pxList.length; i++) {
  940 + const px = pxList[i];
  941 +
  942 + // 验证品项基本信息
  943 + if (!px.px || !px.pxmc) {
  944 + this.$message.error(`第${i + 1}个品项信息不完整,请重新选择`);
  945 + return false;
867 946 }
868   - this.dataForm.lqHytkMxList.push(item);
869   - // 重新计算总金额和总手工费
870   - this.calculateTotalAmount();
871   - this.calculateTotalLaborCost();
872   - },
873   - handleDelLqXhPxmxEntityList(index) {
874   - this.dataForm.lqHytkMxList.splice(index, 1);
875   - // 重新计算总金额和总手工费
876   - this.calculateTotalAmount();
877   - this.calculateTotalLaborCost();
878   - },
879   - validatePxList(){
880   - const pxList = this.dataForm.lqHytkMxList;
881   -
882   - // 1. 验证是否有品项
883   - if (!pxList || pxList.length === 0) {
884   - this.$message.error('请至少添加一个品项');
  947 + if (!px.pxjg || parseFloat(px.pxjg) <= 0) {
  948 + this.$message.error(`第${i + 1}个品项的价格必须大于0`);
885 949 return false;
886 950 }
887   -
888   - // 2. 验证每个品项的信息
889   - for (let i = 0; i < pxList.length; i++) {
890   - const px = pxList[i];
891   -
892   - // 验证品项基本信息
893   - if (!px.px || !px.pxmc) {
894   - this.$message.error(`第${i + 1}个品项信息不完整,请重新选择`);
  951 +
  952 + // 验证退卡次数不能超过剩余次数
  953 + if (px.RemainingCount && px.projectNumber > px.RemainingCount) {
  954 + this.$message.error(`第${i + 1}个品项的退卡次数(${px.projectNumber})不能超过剩余次数(${px.RemainingCount})`);
  955 + return false;
  956 + }
  957 +
  958 + // 验证健康师
  959 + if (!px.lqHytkJksyjList || px.lqHytkJksyjList.length === 0) {
  960 + this.$message.error(`第${i + 1}个品项必须至少选择一个健康师`);
  961 + return false;
  962 + }
  963 +
  964 + // 计算健康师业绩总和
  965 + let jksTotalYj = 0;
  966 + let jksTotalLaborCost = 0;
  967 + let jksTotalNumber = 0;
  968 +
  969 + for (let j = 0; j < px.lqHytkJksyjList.length; j++) {
  970 + const jks = px.lqHytkJksyjList[j];
  971 +
  972 + // 验证健康师是否选择
  973 + if (!jks.jks || !jks.jksxm) {
  974 + this.$message.error(`第${i + 1}个品项的第${j + 1}个健康师必须选择`);
895 975 return false;
896 976 }
897   - if (!px.pxjg || parseFloat(px.pxjg) <= 0) {
898   - this.$message.error(`第${i + 1}个品项的价格必须大于0`);
  977 +
  978 + // 验证健康师业绩必须填写
  979 + if (!jks.jksyj || jks.jksyj.trim() === "") {
  980 + this.$message.error(`第${i + 1}个品项的第${j + 1}个健康师业绩必须填写`);
899 981 return false;
900 982 }
901   -
902   - // 验证退卡次数不能超过剩余次数
903   - if (px.RemainingCount && px.projectNumber > px.RemainingCount) {
904   - this.$message.error(`第${i + 1}个品项的退卡次数(${px.projectNumber})不能超过剩余次数(${px.RemainingCount})`);
  983 +
  984 + // 验证业绩为数字
  985 + const yj = parseFloat(jks.jksyj);
  986 + if (isNaN(yj) || yj < 0) {
  987 + this.$message.error(`第${i + 1}个品项的第${j + 1}个健康师业绩必须为有效数字`);
905 988 return false;
906 989 }
907   -
908   - // 验证健康师
909   - if (!px.lqHytkJksyjList || px.lqHytkJksyjList.length === 0) {
910   - this.$message.error(`第${i + 1}个品项必须至少选择一个健康师`);
  990 +
  991 + jksTotalYj += yj;
  992 + jksTotalLaborCost += parseFloat(jks.laborCost) || 0;
  993 + jksTotalNumber += parseInt(jks.kdpxNumber) || 0;
  994 + }
  995 +
  996 + // 验证健康师业绩总和等于品项金额
  997 + const pxTotalAmount = px.pxjg * px.projectNumber;
  998 + if (Math.abs(jksTotalYj - pxTotalAmount) > 0.01) {
  999 + this.$message.error(`第${i + 1}个品项的健康师业绩总和(${jksTotalYj.toFixed(2)})必须等于品项金额(${pxTotalAmount.toFixed(2)})`);
  1000 + return false;
  1001 + }
  1002 +
  1003 + // 验证健康师手工费总和等于品项手工费
  1004 + const pxTotalLaborCost = (px.sgf || 0) * px.projectNumber;
  1005 + if (Math.abs(jksTotalLaborCost - pxTotalLaborCost) > 0.01) {
  1006 + this.$message.error(`第${i + 1}个品项的健康师手工费总和(${jksTotalLaborCost.toFixed(2)})必须等于品项手工费(${pxTotalLaborCost.toFixed(2)})`);
  1007 + return false;
  1008 + }
  1009 +
  1010 + // 验证健康师次数总和等于品项次数
  1011 + if (jksTotalNumber !== px.projectNumber) {
  1012 + this.$message.error(`第${i + 1}个品项的健康师次数总和(${jksTotalNumber})必须等于品项次数(${px.projectNumber})`);
  1013 + return false;
  1014 + }
  1015 +
  1016 + // 如果是科美品项,验证科技部老师
  1017 + if (px.qt2 === '科美') {
  1018 + if (!px.lqHytkKjbsyjList || px.lqHytkKjbsyjList.length === 0) {
  1019 + this.$message.error(`第${i + 1}个品项是科美品项,必须至少选择一个科技部老师`);
911 1020 return false;
912 1021 }
913   -
914   - // 计算健康师业绩总和
915   - let jksTotalYj = 0;
916   - let jksTotalLaborCost = 0;
917   - let jksTotalNumber = 0;
918   -
919   - for (let j = 0; j < px.lqHytkJksyjList.length; j++) {
920   - const jks = px.lqHytkJksyjList[j];
921   -
922   - // 验证健康师是否选择
923   - if (!jks.jks || !jks.jksxm) {
924   - this.$message.error(`第${i + 1}个品项的第${j + 1}个健康师必须选择`);
  1022 +
  1023 + // 计算科技部老师业绩总和
  1024 + let kjbTotalYj = 0;
  1025 + let kjbTotalLaborCost = 0;
  1026 + let kjbTotalNumber = 0;
  1027 +
  1028 + for (let k = 0; k < px.lqHytkKjbsyjList.length; k++) {
  1029 + const kjb = px.lqHytkKjbsyjList[k];
  1030 +
  1031 + // 验证科技部老师是否选择
  1032 + if (!kjb.kjbls || !kjb.kjblsxm) {
  1033 + this.$message.error(`第${i + 1}个品项的第${k + 1}个科技部老师必须选择`);
925 1034 return false;
926 1035 }
927   -
928   - // 验证健康师业绩必须填写
929   - if (!jks.jksyj || jks.jksyj.trim() === "") {
930   - this.$message.error(`第${i + 1}个品项的第${j + 1}个健康师业绩必须填写`);
  1036 +
  1037 + // 验证科技部老师业绩必须填写
  1038 + if (!kjb.kjblsyj || kjb.kjblsyj.trim() === "") {
  1039 + this.$message.error(`第${i + 1}个品项的第${k + 1}个科技部老师业绩必须填写`);
931 1040 return false;
932 1041 }
933   -
  1042 +
934 1043 // 验证业绩为数字
935   - const yj = parseFloat(jks.jksyj);
  1044 + const yj = parseFloat(kjb.kjblsyj);
936 1045 if (isNaN(yj) || yj < 0) {
937   - this.$message.error(`第${i + 1}个品项的第${j + 1}个健康师业绩必须为有效数字`);
  1046 + this.$message.error(`第${i + 1}个品项的第${k + 1}个科技部老师业绩必须为有效数字`);
938 1047 return false;
939 1048 }
940   -
941   - jksTotalYj += yj;
942   - jksTotalLaborCost += parseFloat(jks.laborCost) || 0;
943   - jksTotalNumber += parseInt(jks.kdpxNumber) || 0;
  1049 +
  1050 + kjbTotalYj += yj;
  1051 + kjbTotalLaborCost += parseFloat(kjb.laborCost) || 0;
  1052 + kjbTotalNumber += parseInt(kjb.hdpxNumber) || 0;
944 1053 }
945   -
946   - // 验证健康师业绩总和等于品项金额
947   - const pxTotalAmount = px.pxjg * px.projectNumber;
948   - if (Math.abs(jksTotalYj - pxTotalAmount) > 0.01) {
949   - this.$message.error(`第${i + 1}个品项的健康师业绩总和(${jksTotalYj.toFixed(2)})必须等于品项金额(${pxTotalAmount.toFixed(2)})`);
  1054 +
  1055 + // 验证科技部老师业绩总和等于品项金额
  1056 + if (Math.abs(kjbTotalYj - pxTotalAmount) > 0.01) {
  1057 + this.$message.error(`第${i + 1}个品项的科技部老师业绩总和(${kjbTotalYj.toFixed(2)})必须等于品项金额(${pxTotalAmount.toFixed(2)})`);
950 1058 return false;
951 1059 }
952   -
953   - // 验证健康师手工费总和等于品项手工费
954   - const pxTotalLaborCost = (px.sgf || 0) * px.projectNumber;
955   - if (Math.abs(jksTotalLaborCost - pxTotalLaborCost) > 0.01) {
956   - this.$message.error(`第${i + 1}个品项的健康师手工费总和(${jksTotalLaborCost.toFixed(2)})必须等于品项手工费(${pxTotalLaborCost.toFixed(2)})`);
  1060 +
  1061 + // 验证科技部老师手工费总和等于品项手工费
  1062 + if (Math.abs(kjbTotalLaborCost - pxTotalLaborCost) > 0.01) {
  1063 + this.$message.error(`第${i + 1}个品项的科技部老师手工费总和(${kjbTotalLaborCost.toFixed(2)})必须等于品项手工费(${pxTotalLaborCost.toFixed(2)})`);
957 1064 return false;
958 1065 }
959   -
960   - // 验证健康师次数总和等于品项次数
961   - if (jksTotalNumber !== px.projectNumber) {
962   - this.$message.error(`第${i + 1}个品项的健康师次数总和(${jksTotalNumber})必须等于品项次数(${px.projectNumber})`);
  1066 +
  1067 + // 验证科技部老师次数总和等于品项次数
  1068 + if (kjbTotalNumber !== px.projectNumber) {
  1069 + this.$message.error(`第${i + 1}个品项的科技部老师次数总和(${kjbTotalNumber})必须等于品项次数(${px.projectNumber})`);
963 1070 return false;
964 1071 }
965   -
966   - // 如果是科美品项,验证科技部老师
967   - if (px.qt2 === '科美') {
968   - if (!px.lqHytkKjbsyjList || px.lqHytkKjbsyjList.length === 0) {
969   - this.$message.error(`第${i + 1}个品项是科美品项,必须至少选择一个科技部老师`);
970   - return false;
971   - }
972   -
973   - // 计算科技部老师业绩总和
974   - let kjbTotalYj = 0;
975   - let kjbTotalLaborCost = 0;
976   - let kjbTotalNumber = 0;
977   -
978   - for (let k = 0; k < px.lqHytkKjbsyjList.length; k++) {
979   - const kjb = px.lqHytkKjbsyjList[k];
980   -
981   - // 验证科技部老师是否选择
982   - if (!kjb.kjbls || !kjb.kjblsxm) {
983   - this.$message.error(`第${i + 1}个品项的第${k + 1}个科技部老师必须选择`);
984   - return false;
985   - }
986   -
987   - // 验证科技部老师业绩必须填写
988   - if (!kjb.kjblsyj || kjb.kjblsyj.trim() === "") {
989   - this.$message.error(`第${i + 1}个品项的第${k + 1}个科技部老师业绩必须填写`);
990   - return false;
991   - }
992   -
993   - // 验证业绩为数字
994   - const yj = parseFloat(kjb.kjblsyj);
995   - if (isNaN(yj) || yj < 0) {
996   - this.$message.error(`第${i + 1}个品项的第${k + 1}个科技部老师业绩必须为有效数字`);
997   - return false;
998   - }
999   -
1000   - kjbTotalYj += yj;
1001   - kjbTotalLaborCost += parseFloat(kjb.laborCost) || 0;
1002   - kjbTotalNumber += parseInt(kjb.hdpxNumber) || 0;
1003   - }
1004   -
1005   - // 验证科技部老师业绩总和等于品项金额
1006   - if (Math.abs(kjbTotalYj - pxTotalAmount) > 0.01) {
1007   - this.$message.error(`第${i + 1}个品项的科技部老师业绩总和(${kjbTotalYj.toFixed(2)})必须等于品项金额(${pxTotalAmount.toFixed(2)})`);
1008   - return false;
1009   - }
1010   -
1011   - // 验证科技部老师手工费总和等于品项手工费
1012   - if (Math.abs(kjbTotalLaborCost - pxTotalLaborCost) > 0.01) {
1013   - this.$message.error(`第${i + 1}个品项的科技部老师手工费总和(${kjbTotalLaborCost.toFixed(2)})必须等于品项手工费(${pxTotalLaborCost.toFixed(2)})`);
1014   - return false;
1015   - }
1016   -
1017   - // 验证科技部老师次数总和等于品项次数
1018   - if (kjbTotalNumber !== px.projectNumber) {
1019   - this.$message.error(`第${i + 1}个品项的科技部老师次数总和(${kjbTotalNumber})必须等于品项次数(${px.projectNumber})`);
1020   - return false;
1021   - }
1022   - }
1023   - }
1024   -
1025   - return true;
1026   - },
1027   - // 处理表单数据
1028   - processFormData() {
1029   - // 检查是否有科美品项
1030   - const hasKemei = this.dataForm.lqHytkMxList.some(px => px.qt2 === '科美');
1031   -
1032   - // 设置是否有科技部
1033   - this.dataForm.sfykjb = hasKemei ? "是" : "否";
1034   -
1035   - // 处理品项列表,转换为指定格式
1036   - this.dataForm.lqHytkMxList = this.dataForm.lqHytkMxList.map(px => {
1037   - // 计算退卡金额和总价
1038   - const tkje = parseFloat(px.pxjg) * parseInt(px.projectNumber) || 0;
1039   - const f_TotalPrice = tkje;
1040   -
1041   - // 处理健康师列表
1042   - const lqHytkJksyjList = (px.lqHytkJksyjList || []).map(jks => ({
1043   - jks: jks.jks || '',
1044   - jksxm: jks.jksxm || '',
1045   - jkszh: jks.jkszh || '',
1046   - jksyj: parseFloat(jks.jksyj) || 0,
1047   - f_jsjid: jks.jsjId || '',
1048   - f_tkpxid: px.px || '',
1049   - f_LaborCost: parseFloat(jks.laborCost) || 0,
1050   - f_tkpxNumber: parseInt(px.projectNumber) || 0
1051   - }));
1052   -
1053   - // 处理科技部老师列表
1054   - const lqHytkKjbsyjList = (px.lqHytkKjbsyjList || []).map(kjb => ({
1055   - kjbls: kjb.kjbls || '',
1056   - kjblsxm: kjb.kjblsxm || '',
1057   - kjblszh: kjb.kjblszh || '',
1058   - kjblsyj: parseFloat(kjb.kjblsyj) || 0,
1059   - f_tkpxid: px.px || '',
1060   - f_LaborCost: parseFloat(kjb.laborCost) || 0,
1061   - f_tkpxNumber: parseInt(px.projectNumber) || 0
1062   - }));
1063   -
1064   - return {
1065   - px: px.px || '',
1066   - pxmc: px.pxmc || '',
1067   - pxjg: parseFloat(px.pxjg) || 0,
1068   - tkje: tkje,
1069   - f_ProjectNumber: parseInt(px.projectNumber) || 0,
1070   - f_SourceType: px.qt2 || '',
1071   - f_TotalPrice: f_TotalPrice,
1072   - lqHytkJksyjList: lqHytkJksyjList,
1073   - lqHytkKjbsyjList: lqHytkKjbsyjList
1074   - };
1075   - });
1076   - },
1077   - // 品项健康师相关方法
1078   - addPxJks(pxIndex) {
1079   - if (!this.dataForm.lqHytkMxList[pxIndex].lqHytkJksyjList) {
1080   - this.$set(this.dataForm.lqHytkMxList[pxIndex], 'lqHytkJksyjList', []);
1081   - }
1082   - // 获取当前品项的手工费
1083   - const currentPx = this.dataForm.lqHytkMxList[pxIndex];
1084   - const pxLaborCost = currentPx.sgf || 0;
1085   - const defaultNumber = 1;
1086   - const laborCost = pxLaborCost * defaultNumber; // 品项手工费 × 次数
1087   -
1088   - let item = {
1089   - "jks": "",
1090   - "jksxm": "",
1091   - "jkszh": "",
1092   - "jksyj": "",
1093   - "jsjId": "",
1094   - "laborCost": laborCost, // 品项手工费 × 次数
1095   - "kdpxNumber": defaultNumber // 默认次数为1
1096 1072 }
1097   - this.dataForm.lqHytkMxList[pxIndex].lqHytkJksyjList.push(item);
1098   - console.log('添加健康师:', {
1099   - pxLaborCost,
1100   - defaultNumber,
1101   - laborCost,
1102   - formula: `${pxLaborCost} × ${defaultNumber} = ${laborCost}`
1103   - });
1104   - },
1105   - removePxJks(pxIndex, jksIndex) {
1106   - this.dataForm.lqHytkMxList[pxIndex].lqHytkJksyjList.splice(jksIndex, 1);
1107   - },
1108   - // 品项科技部老师相关方法
1109   - addPxKjb(pxIndex) {
1110   - if (!this.dataForm.lqHytkMxList[pxIndex].lqHytkKjbsyjList) {
1111   - this.$set(this.dataForm.lqHytkMxList[pxIndex], 'lqHytkKjbsyjList', []);
1112   - }
1113   - // 获取当前品项的手工费
1114   - const currentPx = this.dataForm.lqHytkMxList[pxIndex];
1115   - const pxLaborCost = currentPx.sgf || 0;
1116   - const defaultNumber = 1;
1117   - const laborCost = pxLaborCost * defaultNumber; // 品项手工费 × 次数
1118   -
1119   - let item = {
1120   - "kjbls": "",
1121   - "kjblsyj": "",
1122   - "kjblsxm": "",
1123   - "kjblszh": "",
1124   - "laborCost": laborCost, // 品项手工费 × 次数
1125   - "hdpxNumber": defaultNumber // 默认次数为1
1126   - }
1127   - this.dataForm.lqHytkMxList[pxIndex].lqHytkKjbsyjList.push(item);
1128   - console.log('添加科技部老师:', {
1129   - pxLaborCost,
1130   - defaultNumber,
1131   - laborCost,
1132   - formula: `${pxLaborCost} × ${defaultNumber} = ${laborCost}`
1133   - });
1134   - },
1135   - removePxKjb(pxIndex, kjbIndex) {
1136   - this.dataForm.lqHytkMxList[pxIndex].lqHytkKjbsyjList.splice(kjbIndex, 1);
1137   - },
1138   - // 加载门店数据并设置默认门店
1139   - loadStoreDataAndSetDefault() {
1140   - console.log('开始加载门店数据和用户信息...');
1141   - // 先加载门店数据
1142   - this.loadStoreDataPromise().then(() => {
1143   - // 门店数据加载完成后,获取用户信息
1144   - return this.getCurrentUserStorePromise();
1145   - }).then((userData) => {
1146   - if (userData && userData.mdid) {
1147   - const userStoreId = userData.mdid;
1148   - console.log('用户门店ID:', userStoreId);
1149   - console.log('门店选项列表:', this.mdOptions);
1150   -
1151   - // 检查用户门店ID是否在门店列表中
1152   - const matchingStore = this.mdOptions.find(store => store.id === userStoreId);
1153   - if (matchingStore) {
1154   - this.dataForm.md = userStoreId;
1155   - this.dataForm.mdmc = matchingStore.fullName;
1156   - this.dataForm.mdbh = matchingStore.id;
1157   - console.log('找到匹配的门店,设置默认门店ID:', userStoreId);
1158   - console.log('匹配的门店信息:', matchingStore);
1159   - } else {
1160   - console.warn('用户门店ID在门店列表中未找到匹配项');
1161   - console.log('用户门店ID:', userStoreId);
1162   - console.log('可用门店ID列表:', this.mdOptions.map(s => s.id));
1163   - }
1164   - } else {
1165   - console.warn('用户数据中没有门店ID,无法设置默认值');
1166   - }
1167   -
1168   - // 加载健康师和科技部老师选项
1169   - this.getjksOptions();
1170   - this.getkjbOptions();
1171   - }).catch(err => {
1172   - console.error('加载数据失败:', err);
1173   - });
1174   - },
1175   - // 加载门店数据
1176   - loadStoreDataPromise() {
1177   - // 使用原来的数据接口方式
1178   - return previewDataInterface('730960205902251269').then(res => {
1179   - console.log('门店API原始响应:', res);
1180   - if (res.data && res.data.length > 0) {
1181   - this.mdOptions = res.data;
1182   - console.log('门店数据加载成功:', this.mdOptions);
1183   - return res.data;
  1073 + }
  1074 +
  1075 + return true;
  1076 + },
  1077 + // 处理表单数据
  1078 + processFormData() {
  1079 + // 检查是否有科美品项
  1080 + const hasKemei = this.dataForm.lqHytkMxList.some(px => px.qt2 === '科美');
  1081 +
  1082 + // 设置是否有科技部
  1083 + this.dataForm.sfykjb = hasKemei ? "是" : "否";
  1084 +
  1085 + // 处理品项列表,转换为指定格式
  1086 + this.dataForm.lqHytkMxList = this.dataForm.lqHytkMxList.map(px => {
  1087 + // 计算退卡金额和总价
  1088 + const tkje = parseFloat(px.pxjg) * parseInt(px.projectNumber) || 0;
  1089 + const f_TotalPrice = tkje;
  1090 +
  1091 + // 处理健康师列表
  1092 + const lqHytkJksyjList = (px.lqHytkJksyjList || []).map(jks => ({
  1093 + jks: jks.jks || '',
  1094 + jksxm: jks.jksxm || '',
  1095 + jkszh: jks.jkszh || '',
  1096 + jksyj: parseFloat(jks.jksyj) || 0,
  1097 + f_jsjid: jks.jsjId || '',
  1098 + f_tkpxid: px.px || '',
  1099 + f_LaborCost: parseFloat(jks.laborCost) || 0,
  1100 + f_tkpxNumber: parseInt(px.projectNumber) || 0
  1101 + }));
  1102 +
  1103 + // 处理科技部老师列表
  1104 + const lqHytkKjbsyjList = (px.lqHytkKjbsyjList || []).map(kjb => ({
  1105 + kjbls: kjb.kjbls || '',
  1106 + kjblsxm: kjb.kjblsxm || '',
  1107 + kjblszh: kjb.kjblszh || '',
  1108 + kjblsyj: parseFloat(kjb.kjblsyj) || 0,
  1109 + f_tkpxid: px.px || '',
  1110 + f_LaborCost: parseFloat(kjb.laborCost) || 0,
  1111 + f_tkpxNumber: parseInt(px.projectNumber) || 0
  1112 + }));
  1113 +
  1114 + return {
  1115 + px: px.px || '',
  1116 + pxmc: px.pxmc || '',
  1117 + pxjg: parseFloat(px.pxjg) || 0,
  1118 + tkje: tkje,
  1119 + f_ProjectNumber: parseInt(px.projectNumber) || 0,
  1120 + f_SourceType: px.qt2 || '',
  1121 + f_TotalPrice: f_TotalPrice,
  1122 + lqHytkJksyjList: lqHytkJksyjList,
  1123 + lqHytkKjbsyjList: lqHytkKjbsyjList
  1124 + };
  1125 + });
  1126 + },
  1127 + // 品项健康师相关方法
  1128 + addPxJks(pxIndex) {
  1129 + if (!this.dataForm.lqHytkMxList[pxIndex].lqHytkJksyjList) {
  1130 + this.$set(this.dataForm.lqHytkMxList[pxIndex], 'lqHytkJksyjList', []);
  1131 + }
  1132 + // 获取当前品项的手工费
  1133 + const currentPx = this.dataForm.lqHytkMxList[pxIndex];
  1134 + const pxLaborCost = currentPx.sgf || 0;
  1135 + const defaultNumber = 1;
  1136 + const laborCost = pxLaborCost * defaultNumber; // 品项手工费 × 次数
  1137 +
  1138 + let item = {
  1139 + "jks": "",
  1140 + "jksxm": "",
  1141 + "jkszh": "",
  1142 + "jksyj": "",
  1143 + "jsjId": "",
  1144 + "laborCost": laborCost, // 品项手工费 × 次数
  1145 + "kdpxNumber": defaultNumber // 默认次数为1
  1146 + }
  1147 + this.dataForm.lqHytkMxList[pxIndex].lqHytkJksyjList.push(item);
  1148 + console.log('添加健康师:', {
  1149 + pxLaborCost,
  1150 + defaultNumber,
  1151 + laborCost,
  1152 + formula: `${pxLaborCost} × ${defaultNumber} = ${laborCost}`
  1153 + });
  1154 + },
  1155 + removePxJks(pxIndex, jksIndex) {
  1156 + this.dataForm.lqHytkMxList[pxIndex].lqHytkJksyjList.splice(jksIndex, 1);
  1157 + },
  1158 + // 品项科技部老师相关方法
  1159 + addPxKjb(pxIndex) {
  1160 + if (!this.dataForm.lqHytkMxList[pxIndex].lqHytkKjbsyjList) {
  1161 + this.$set(this.dataForm.lqHytkMxList[pxIndex], 'lqHytkKjbsyjList', []);
  1162 + }
  1163 + // 获取当前品项的手工费
  1164 + const currentPx = this.dataForm.lqHytkMxList[pxIndex];
  1165 + const pxLaborCost = currentPx.sgf || 0;
  1166 + const defaultNumber = 1;
  1167 + const laborCost = pxLaborCost * defaultNumber; // 品项手工费 × 次数
  1168 +
  1169 + let item = {
  1170 + "kjbls": "",
  1171 + "kjblsyj": "",
  1172 + "kjblsxm": "",
  1173 + "kjblszh": "",
  1174 + "laborCost": laborCost, // 品项手工费 × 次数
  1175 + "hdpxNumber": defaultNumber // 默认次数为1
  1176 + }
  1177 + this.dataForm.lqHytkMxList[pxIndex].lqHytkKjbsyjList.push(item);
  1178 + console.log('添加科技部老师:', {
  1179 + pxLaborCost,
  1180 + defaultNumber,
  1181 + laborCost,
  1182 + formula: `${pxLaborCost} × ${defaultNumber} = ${laborCost}`
  1183 + });
  1184 + },
  1185 + removePxKjb(pxIndex, kjbIndex) {
  1186 + this.dataForm.lqHytkMxList[pxIndex].lqHytkKjbsyjList.splice(kjbIndex, 1);
  1187 + },
  1188 + // 加载门店数据并设置默认门店
  1189 + loadStoreDataAndSetDefault() {
  1190 + console.log('开始加载门店数据和用户信息...');
  1191 + // 先加载门店数据
  1192 + this.loadStoreDataPromise().then(() => {
  1193 + // 门店数据加载完成后,获取用户信息
  1194 + return this.getCurrentUserStorePromise();
  1195 + }).then((userData) => {
  1196 + if (userData && userData.mdid) {
  1197 + const userStoreId = userData.mdid;
  1198 + console.log('用户门店ID:', userStoreId);
  1199 + console.log('门店选项列表:', this.mdOptions);
  1200 +
  1201 + // 检查用户门店ID是否在门店列表中
  1202 + const matchingStore = this.mdOptions.find(store => store.id === userStoreId);
  1203 + if (matchingStore) {
  1204 + this.dataForm.md = userStoreId;
  1205 + this.dataForm.mdmc = matchingStore.fullName;
  1206 + this.dataForm.mdbh = matchingStore.id;
  1207 + console.log('找到匹配的门店,设置默认门店ID:', userStoreId);
  1208 + console.log('匹配的门店信息:', matchingStore);
1184 1209 } else {
1185   - console.warn('门店数据为空');
1186   - return [];
  1210 + console.warn('用户门店ID在门店列表中未找到匹配项');
  1211 + console.log('用户门店ID:', userStoreId);
  1212 + console.log('可用门店ID列表:', this.mdOptions.map(s => s.id));
1187 1213 }
1188   - }).catch(err => {
1189   - console.error('门店数据加载失败:', err);
  1214 + } else {
  1215 + console.warn('用户数据中没有门店ID,无法设置默认值');
  1216 + }
  1217 +
  1218 + // 加载健康师和科技部老师选项
  1219 + this.getjksOptions();
  1220 + this.getkjbOptions();
  1221 + }).catch(err => {
  1222 + console.error('加载数据失败:', err);
  1223 + });
  1224 + },
  1225 + // 加载门店数据
  1226 + loadStoreDataPromise() {
  1227 + // 使用原来的数据接口方式
  1228 + return previewDataInterface('730960205902251269').then(res => {
  1229 + console.log('门店API原始响应:', res);
  1230 + if (res.data && res.data.length > 0) {
  1231 + this.mdOptions = res.data;
  1232 + console.log('门店数据加载成功:', this.mdOptions);
  1233 + return res.data;
  1234 + } else {
  1235 + console.warn('门店数据为空');
1190 1236 return [];
  1237 + }
  1238 + }).catch(err => {
  1239 + console.error('门店数据加载失败:', err);
  1240 + return [];
  1241 + });
  1242 + },
  1243 + // 获取当前用户门店信息
  1244 + getCurrentUserStorePromise() {
  1245 + return new Promise((resolve, reject) => {
  1246 + getInfo().then(res => {
  1247 + resolve(res.data.userInfo);
  1248 + }).catch(err => {
  1249 + console.error('获取当前用户信息失败:', err);
  1250 + reject(err);
1191 1251 });
1192   - },
1193   - // 获取当前用户门店信息
1194   - getCurrentUserStorePromise() {
1195   - return new Promise((resolve, reject) => {
1196   - getInfo().then(res => {
1197   - resolve(res.data.userInfo);
1198   - }).catch(err => {
1199   - console.error('获取当前用户信息失败:', err);
1200   - reject(err);
1201   - });
1202   - });
1203   - },
1204   - }
  1252 + });
  1253 + },
1205 1254 }
  1255 +}
1206 1256 </script>
1207 1257  
1208 1258 <style scoped>
... ... @@ -1577,18 +1627,18 @@
1577 1627 width: 100%;
1578 1628 min-width: auto;
1579 1629 }
1580   -
  1630 +
1581 1631 .remaining-item-row {
1582 1632 flex-wrap: wrap;
1583 1633 gap: 8px;
1584 1634 }
1585   -
  1635 +
1586 1636 .item-name {
1587 1637 width: 100%;
1588 1638 margin-right: 0;
1589 1639 margin-bottom: 4px;
1590 1640 }
1591   -
  1641 +
1592 1642 .item-price,
1593 1643 .item-quantity,
1594 1644 .item-source {
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdDeductinfo/LqKdDeductinfoListOutput.cs
... ... @@ -95,5 +95,20 @@ namespace NCC.Extend.Entitys.Dto.LqKdDeductinfo
95 95 /// 门店名称
96 96 /// </summary>
97 97 public string StoreName { get; set; }
  98 +
  99 + /// <summary>
  100 + /// 时间期间(开单日期,用于时间范围筛选)
  101 + /// </summary>
  102 + public DateTime? TimePeriod { get; set; }
  103 +
  104 + /// <summary>
  105 + /// 开单类型(购买、赠送、体验等)
  106 + /// </summary>
  107 + public string BillingType { get; set; }
  108 +
  109 + /// <summary>
  110 + /// 合作机构
  111 + /// </summary>
  112 + public string CooperationInstitution { get; set; }
98 113 }
99 114 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/BillingRecordSummaryQueryInput.cs
... ... @@ -27,5 +27,29 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
27 27 /// </summary>
28 28 [Display(Name = "结束时间", Description = "查询开单记录的结束时间")]
29 29 public DateTime? EndTime { get; set; }
  30 +
  31 + /// <summary>
  32 + /// 品项分类
  33 + /// </summary>
  34 + [Display(Name = "品项分类", Description = "筛选品项分类")]
  35 + public string ItemCategory { get; set; }
  36 +
  37 + /// <summary>
  38 + /// 品项ID
  39 + /// </summary>
  40 + [Display(Name = "品项ID", Description = "筛选品项ID")]
  41 + public string ItemId { get; set; }
  42 +
  43 + /// <summary>
  44 + /// 健康师ID
  45 + /// </summary>
  46 + [Display(Name = "健康师ID", Description = "筛选健康师ID")]
  47 + public string HealthCoachId { get; set; }
  48 +
  49 + /// <summary>
  50 + /// 客户ID(会员ID)
  51 + /// </summary>
  52 + [Display(Name = "客户ID", Description = "筛选客户ID")]
  53 + public string MemberId { get; set; }
30 54 }
31 55 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqKdKdjlb/HealthCoachStatisticsOutput.cs
... ... @@ -94,5 +94,20 @@ namespace NCC.Extend.Entitys.Dto.LqKdKdjlb
94 94 /// 退卡金额 - 统计该健康师在指定时间周期内的退卡业绩总金额
95 95 /// </summary>
96 96 public decimal refundAmount { get; set; }
  97 +
  98 + /// <summary>
  99 + /// 手工费 - 统计该健康师在指定时间周期内消耗时的手工费总金额
  100 + /// </summary>
  101 + public decimal laborCost { get; set; }
  102 +
  103 + /// <summary>
  104 + /// 原始手工费 - 统计该健康师在指定时间周期内消耗时的原始手工费总金额
  105 + /// </summary>
  106 + public decimal originalLaborCost { get; set; }
  107 +
  108 + /// <summary>
  109 + /// 加班手工费 - 统计该健康师在指定时间周期内消耗时的加班手工费总金额
  110 + /// </summary>
  111 + public decimal overtimeLaborCost { get; set; }
97 112 }
98 113 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryInput.cs 0 → 100644
  1 +using NCC.Common.Filter;
  2 +using System;
  3 +
  4 +namespace NCC.Extend.Entitys.Dto.LqSalary
  5 +{
  6 + /// <summary>
  7 + /// 健康师工资查询参数
  8 + /// </summary>
  9 + public class HealthCoachSalaryInput : PageInputBase
  10 + {
  11 + /// <summary>
  12 + /// 年份
  13 + /// </summary>
  14 + public int Year { get; set; }
  15 +
  16 + /// <summary>
  17 + /// 月份
  18 + /// </summary>
  19 + public int Month { get; set; }
  20 +
  21 + /// <summary>
  22 + /// 门店ID(可选,用于筛选特定门店)
  23 + /// </summary>
  24 + public string StoreId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 健康师姓名/账号(可选,用于模糊搜索)
  28 + /// </summary>
  29 + public string Keyword { get; set; }
  30 + }
  31 +}
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqSalary/HealthCoachSalaryOutput.cs 0 → 100644
  1 +using System;
  2 +
  3 +namespace NCC.Extend.Entitys.Dto.LqSalary
  4 +{
  5 + /// <summary>
  6 + /// 健康师工资输出
  7 + /// </summary>
  8 + public class HealthCoachSalaryOutput
  9 + {
  10 + /// <summary>
  11 + /// 主键ID
  12 + /// </summary>
  13 + public string Id { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 门店名称
  17 + /// </summary>
  18 + public string StoreName { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 员工姓名
  22 + /// </summary>
  23 + public string EmployeeName { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 岗位
  27 + /// </summary>
  28 + public string Position { get; set; }
  29 +
  30 + /// <summary>
  31 + /// 金三角战队
  32 + /// </summary>
  33 + public string GoldTriangleTeam { get; set; }
  34 +
  35 + /// <summary>
  36 + /// 总业绩
  37 + /// </summary>
  38 + public decimal TotalPerformance { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 基础业绩
  42 + /// </summary>
  43 + public decimal BasePerformance { get; set; }
  44 +
  45 + /// <summary>
  46 + /// 合作业绩
  47 + /// </summary>
  48 + public decimal CooperationPerformance { get; set; }
  49 +
  50 + /// <summary>
  51 + /// 奖励业绩
  52 + /// </summary>
  53 + public decimal RewardPerformance { get; set; }
  54 +
  55 + /// <summary>
  56 + /// 消耗
  57 + /// </summary>
  58 + public decimal Consumption { get; set; }
  59 +
  60 + /// <summary>
  61 + /// 项目数
  62 + /// </summary>
  63 + public decimal ProjectCount { get; set; }
  64 +
  65 + /// <summary>
  66 + /// 到店人头
  67 + /// </summary>
  68 + public decimal CustomerCount { get; set; }
  69 +
  70 + /// <summary>
  71 + /// 在店天数
  72 + /// </summary>
  73 + public decimal WorkingDays { get; set; }
  74 +
  75 + /// <summary>
  76 + /// 健康师底薪
  77 + /// </summary>
  78 + public decimal HealthCoachBaseSalary { get; set; }
  79 +
  80 + /// <summary>
  81 + /// 提成合计
  82 + /// </summary>
  83 + public decimal TotalCommission { get; set; }
  84 +
  85 + /// <summary>
  86 + /// 手工费
  87 + /// </summary>
  88 + public decimal HandworkFee { get; set; }
  89 +
  90 + /// <summary>
  91 + /// 补贴合计
  92 + /// </summary>
  93 + public decimal TotalSubsidy { get; set; }
  94 +
  95 + /// <summary>
  96 + /// 扣款合计
  97 + /// </summary>
  98 + public decimal TotalDeduction { get; set; }
  99 +
  100 + /// <summary>
  101 + /// 实发工资
  102 + /// </summary>
  103 + public decimal ActualSalary { get; set; }
  104 +
  105 + /// <summary>
  106 + /// 是否锁定
  107 + /// </summary>
  108 + public int IsLocked { get; set; }
  109 +
  110 + /// <summary>
  111 + /// 更新时间
  112 + /// </summary>
  113 + public DateTime UpdateTime { get; set; }
  114 + }
  115 +}
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_jksyj/LqHytkJksyjEntity.cs
... ... @@ -137,5 +137,11 @@ namespace NCC.Extend.Entitys.lq_hytk_jksyj
137 137 /// </summary>
138 138 [SugarColumn(ColumnName = "F_ItemName")]
139 139 public string ItemName { get; set; }
  140 +
  141 + /// <summary>
  142 + /// 业绩类型
  143 + /// </summary>
  144 + [SugarColumn(ColumnName = "F_PerformanceType")]
  145 + public string PerformanceType { get; set; }
140 146 }
141 147 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_kjbsyj/LqHytkKjbsyjEntity.cs
... ... @@ -132,5 +132,11 @@ namespace NCC.Extend.Entitys.lq_hytk_kjbsyj
132 132 /// </summary>
133 133 [SugarColumn(ColumnName = "F_ItemName")]
134 134 public string ItemName { get; set; }
  135 +
  136 + /// <summary>
  137 + /// 业绩类型
  138 + /// </summary>
  139 + [SugarColumn(ColumnName = "F_PerformanceType")]
  140 + public string PerformanceType { get; set; }
135 141 }
136 142 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_hytk_mx/LqHytkMxEntity.cs
... ... @@ -120,5 +120,11 @@ namespace NCC.Extend.Entitys.lq_hytk_mx
120 120 /// </summary>
121 121 [SugarColumn(ColumnName = "F_IsEffective")]
122 122 public int IsEffective { get; set; } = StatusEnum.有效.GetHashCode();
  123 +
  124 + /// <summary>
  125 + /// 业绩类型
  126 + /// </summary>
  127 + [SugarColumn(ColumnName = "F_PerformanceType")]
  128 + public string PerformanceType { get; set; }
123 129 }
124 130 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_jksyj/LqKdJksyjEntity.cs
... ... @@ -100,5 +100,11 @@ namespace NCC.Extend.Entitys.lq_kd_jksyj
100 100 /// </summary>
101 101 [SugarColumn(ColumnName = "F_ItemName")]
102 102 public string ItemName { get; set; }
  103 +
  104 + /// <summary>
  105 + /// 业绩类型
  106 + /// </summary>
  107 + [SugarColumn(ColumnName = "F_PerformanceType")]
  108 + public string PerformanceType { get; set; }
103 109 }
104 110 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_kjbsyj/LqKdKjbsyjEntity.cs
... ... @@ -100,5 +100,11 @@ namespace NCC.Extend.Entitys.lq_kd_kjbsyj
100 100 /// </summary>
101 101 [SugarColumn(ColumnName = "F_ItemName")]
102 102 public string ItemName { get; set; }
  103 +
  104 + /// <summary>
  105 + /// 业绩类型
  106 + /// </summary>
  107 + [SugarColumn(ColumnName = "F_PerformanceType")]
  108 + public string PerformanceType { get; set; }
103 109 }
104 110 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_kd_pxmx/LqKdPxmxEntity.cs
... ... @@ -113,5 +113,11 @@ namespace NCC.Extend.Entitys.lq_kd_pxmx
113 113 /// </summary>
114 114 [SugarColumn(ColumnName = "F_ActivityId")]
115 115 public string ActivityId { get; set; }
  116 +
  117 + /// <summary>
  118 + /// 业绩类型
  119 + /// </summary>
  120 + [SugarColumn(ColumnName = "F_PerformanceType")]
  121 + public string PerformanceType { get; set; }
116 122 }
117 123 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_jksyj/LqXhJksyjEntity.cs
... ... @@ -148,5 +148,11 @@ namespace NCC.Extend.Entitys.lq_xh_jksyj
148 148 /// </summary>
149 149 [SugarColumn(ColumnName = "F_ItemName")]
150 150 public string ItemName { get; set; }
  151 +
  152 + /// <summary>
  153 + /// 业绩类型
  154 + /// </summary>
  155 + [SugarColumn(ColumnName = "F_PerformanceType")]
  156 + public string PerformanceType { get; set; }
151 157 }
152 158 }
153 159 \ No newline at end of file
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_kjbsyj/LqXhKjbsyjEntity.cs
... ... @@ -130,5 +130,11 @@ namespace NCC.Extend.Entitys.lq_xh_kjbsyj
130 130 /// </summary>
131 131 [SugarColumn(ColumnName = "F_ItemName")]
132 132 public string ItemName { get; set; }
  133 +
  134 + /// <summary>
  135 + /// 业绩类型
  136 + /// </summary>
  137 + [SugarColumn(ColumnName = "F_PerformanceType")]
  138 + public string PerformanceType { get; set; }
133 139 }
134 140 }
135 141 \ No newline at end of file
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_xh_pxmx/LqXhPxmxEntity.cs
... ... @@ -106,5 +106,11 @@ namespace NCC.Extend.Entitys.lq_xh_pxmx
106 106 /// </summary>
107 107 [SugarColumn(ColumnName = "F_IsEffective")]
108 108 public int? IsEffective { get; set; } = 1;
  109 +
  110 + /// <summary>
  111 + /// 业绩类型
  112 + /// </summary>
  113 + [SugarColumn(ColumnName = "F_PerformanceType")]
  114 + public string PerformanceType { get; set; }
109 115 }
110 116 }
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_yjmxb/LqYjmxbEntity.cs
... ... @@ -112,6 +112,12 @@ namespace NCC.Extend.Entitys.lq_yjmxb
112 112 /// </summary>
113 113 [SugarColumn(ColumnName = "zyj")]
114 114 public string Zyj { get; set; }
  115 +
  116 + /// <summary>
  117 + /// 业绩类型
  118 + /// </summary>
  119 + [SugarColumn(ColumnName = "F_PerformanceType")]
  120 + public string PerformanceType { get; set; }
115 121  
116 122 }
117 123 }
118 124 \ No newline at end of file
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqAttendanceSummaryService.cs
... ... @@ -374,6 +374,169 @@ namespace NCC.Extend
374 374 }
375 375 #endregion
376 376  
  377 + #region 生成健康师考勤模拟数据
  378 + /// <summary>
  379 + /// 生成健康师考勤模拟数据
  380 + /// </summary>
  381 + /// <remarks>
  382 + /// 为所有健康师生成指定年月的考勤模拟数据
  383 + ///
  384 + /// 规则:
  385 + /// - 80%以上的健康师出勤天数 >= 21天
  386 + /// - 其他健康师出勤天数随机生成(0-20天)
  387 + /// - 员工状态默认为1(在职)
  388 + ///
  389 + /// 示例请求:
  390 + /// ```json
  391 + /// {
  392 + /// "year": 2025,
  393 + /// "month": 11
  394 + /// }
  395 + /// ```
  396 + /// </remarks>
  397 + /// <param name="year">年份</param>
  398 + /// <param name="month">月份</param>
  399 + /// <returns>生成结果</returns>
  400 + /// <response code="200">成功生成考勤模拟数据</response>
  401 + /// <response code="400">参数错误</response>
  402 + /// <response code="500">生成失败</response>
  403 + [HttpPost("GenerateMockData/{year}/{month}")]
  404 + public async Task<dynamic> GenerateMockData(int year, int month)
  405 + {
  406 + try
  407 + {
  408 + // 验证参数
  409 + if (year < 2000 || year > 3000)
  410 + {
  411 + throw NCCException.Oh("年份参数无效");
  412 + }
  413 + if (month < 1 || month > 12)
  414 + {
  415 + throw NCCException.Oh("月份参数无效,应在1-12之间");
  416 + }
  417 +
  418 + // 查询所有健康师
  419 + var healthCoaches = await _db.Queryable<UserEntity>()
  420 + .Where(u => u.Gw == "健康师" && u.EnabledMark == 1 && (u.DeleteMark == null || u.DeleteMark == 0))
  421 + .Select(u => new { u.Id, u.RealName })
  422 + .ToListAsync();
  423 +
  424 + if (!healthCoaches.Any())
  425 + {
  426 + throw NCCException.Oh("未找到健康师用户");
  427 + }
  428 +
  429 + // 检查是否已存在该年月的考勤数据
  430 + var existingRecords = await _db.Queryable<LqAttendanceSummaryEntity>()
  431 + .Where(a => a.Year == year && a.Month == month)
  432 + .CountAsync();
  433 +
  434 + if (existingRecords > 0)
  435 + {
  436 + throw NCCException.Oh($"已存在{year}年{month}月的考勤数据,请先清空后再生成");
  437 + }
  438 +
  439 + var totalCount = healthCoaches.Count;
  440 + var highAttendanceCount = (int)Math.Ceiling(totalCount * 0.8); // 80%以上,向上取整
  441 + var lowAttendanceCount = totalCount - highAttendanceCount;
  442 +
  443 + var random = new Random();
  444 + var entitiesToInsert = new List<LqAttendanceSummaryEntity>();
  445 + var now = DateTime.Now;
  446 +
  447 + // 生成考勤数据
  448 + for (int i = 0; i < totalCount; i++)
  449 + {
  450 + var healthCoach = healthCoaches[i];
  451 + decimal workDays;
  452 + decimal leaveDays;
  453 + decimal restDays;
  454 +
  455 + if (i < highAttendanceCount)
  456 + {
  457 + // 80%以上的健康师:出勤天数 >= 21天
  458 + workDays = random.Next(21, 31); // 21-30天
  459 + var remainingDays = 30 - workDays;
  460 + // 剩余天数分配给请假和休息
  461 + if (remainingDays > 0)
  462 + {
  463 + leaveDays = random.Next(0, (int)remainingDays + 1);
  464 + restDays = remainingDays - leaveDays;
  465 + }
  466 + else
  467 + {
  468 + leaveDays = 0;
  469 + restDays = 0;
  470 + }
  471 + }
  472 + else
  473 + {
  474 + // 其他20%的健康师:出勤天数 < 21天
  475 + workDays = random.Next(0, 21); // 0-20天
  476 + var remainingDays = 30 - workDays;
  477 + // 剩余天数分配给请假和休息
  478 + if (remainingDays > 0)
  479 + {
  480 + leaveDays = random.Next(0, (int)remainingDays + 1);
  481 + restDays = remainingDays - leaveDays;
  482 + }
  483 + else
  484 + {
  485 + leaveDays = 0;
  486 + restDays = 0;
  487 + }
  488 + }
  489 +
  490 + var entity = new LqAttendanceSummaryEntity
  491 + {
  492 + Id = YitIdHelper.NextId().ToString(),
  493 + UserId = healthCoach.Id,
  494 + Year = year,
  495 + Month = month,
  496 + EmployeeStatus = 1, // 在职
  497 + WorkDays = workDays,
  498 + LeaveDays = leaveDays,
  499 + RestDays = restDays,
  500 + Remark = "模拟数据",
  501 + CreateUser = _userManager.UserId ?? "system",
  502 + CreateTime = now,
  503 + UpdateUser = _userManager.UserId ?? "system",
  504 + UpdateTime = now,
  505 + IsEffective = 1
  506 + };
  507 + entitiesToInsert.Add(entity);
  508 + }
  509 +
  510 + // 批量插入
  511 + await _db.Insertable(entitiesToInsert).ExecuteCommandAsync();
  512 +
  513 + // 统计结果
  514 + var highAttendanceActual = entitiesToInsert.Count(e => e.WorkDays >= 21);
  515 + var highAttendancePercentage = (decimal)highAttendanceActual / totalCount * 100;
  516 +
  517 + return new
  518 + {
  519 + success = true,
  520 + message = "生成考勤模拟数据成功",
  521 + data = new
  522 + {
  523 + year = year,
  524 + month = month,
  525 + totalCount = totalCount,
  526 + highAttendanceCount = highAttendanceActual,
  527 + highAttendancePercentage = Math.Round(highAttendancePercentage, 2),
  528 + generatedCount = entitiesToInsert.Count
  529 + }
  530 + };
  531 + }
  532 + catch (Exception ex)
  533 + {
  534 + _logger.LogError(ex, $"生成健康师考勤模拟数据失败 - 年份: {year}, 月份: {month}");
  535 + throw NCCException.Oh($"生成健康师考勤模拟数据失败:{ex.Message}");
  536 + }
  537 + }
  538 + #endregion
  539 +
377 540  
378 541 }
379 542 }
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
... ... @@ -299,60 +299,6 @@ namespace NCC.Extend.LqHytkHytk
299 299 /// 创建退卡信息及其关联的品项明细、健康师业绩、科技部老师业绩信息
300 300 /// </summary>
301 301 /// <remarks>
302   - /// 创建退卡记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
303   - ///
304   - /// 示例请求:
305   - /// ```json
306   - /// {
307   - /// "md": "门店ID",
308   - /// "mdbh": "门店编号",
309   - /// "mdmc": "门店名称",
310   - /// "hy": "会员ID",
311   - /// "hymc": "会员姓名",
312   - /// "hyzh": "会员账号",
313   - /// "gklx": "顾客类型",
314   - /// "tkje": 1000.00,
315   - /// "sgfy": 50.00,
316   - /// "bz": "备注",
317   - /// "tkzt": "退卡状态",
318   - /// "tkyy": "退卡原因",
319   - /// "lqHytkMxList": [
320   - /// {
321   - /// "px": "品项编号",
322   - /// "pxmc": "品项名称",
323   - /// "pxjg": 100.00,
324   - /// "tkje": 100.00,
325   - /// "F_ProjectNumber": 1,
326   - /// "F_SourceType": "来源类型",
327   - /// "F_TotalPrice": 100.00,
328   - /// "lqHytkJksyjList": [
329   - /// {
330   - /// "jks": "健康师",
331   - /// "jksxm": "健康师姓名",
332   - /// "jkszh": "健康师账号",
333   - /// "jksyj": 50.00,
334   - /// "F_jsjid": "金三角ID",
335   - /// "F_tkpxid": "项目资料ID",
336   - /// "F_LaborCost": 10.00,
337   - /// "F_tkpxNumber": 1
338   - /// }
339   - /// ],
340   - /// "lqHytkKjbsyjList": [
341   - /// {
342   - /// "kjbls": "科技部老师",
343   - /// "kjblsxm": "科技部老师姓名",
344   - /// "kjblszh": "科技部老师账号",
345   - /// "kjblsyj": 30.00,
346   - /// "F_tkpxid": "项目资料ID",
347   - /// "F_LaborCost": 5.00,
348   - /// "F_tkpxNumber": 1
349   - /// }
350   - /// ]
351   - /// }
352   - /// ]
353   - /// }
354   - /// ```
355   - ///
356 302 /// 参数说明:
357 303 /// - md: 门店ID
358 304 /// - hy: 会员ID
... ... @@ -378,15 +324,12 @@ namespace NCC.Extend.LqHytkHytk
378 324 {
379 325 // 开启事务
380 326 _db.BeginTran();
381   -
382 327 // 新增退卡主表记录
383 328 var newEntity = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteReturnEntityAsync();
384   -
385 329 // 收集所有需要插入的实体,然后批量插入
386 330 var allMxEntities = new List<LqHytkMxEntity>();
387 331 var allJksyjEntities = new List<LqHytkJksyjEntity>();
388 332 var allKjbsyjEntities = new List<LqHytkKjbsyjEntity>();
389   -
390 333 // 处理品项明细列表
391 334 if (input.lqHytkMxList != null && input.lqHytkMxList.Any())
392 335 {
... ... @@ -405,11 +348,13 @@ namespace NCC.Extend.LqHytkHytk
405 348 Pxmc = item.pxmc,
406 349 Pxjg = item.pxjg,
407 350 Tkje = item.tkje,
  351 + Tksj = input.tksj,
408 352 ProjectNumber = item.F_ProjectNumber ?? 1,
409 353 SourceType = item.F_SourceType,
410 354 TotalPrice = item.F_TotalPrice ?? (item.pxjg * (item.F_ProjectNumber ?? 1)),
411 355 IsEffective = StatusEnum.有效.GetHashCode(),
412 356 ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
  357 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "",
413 358 };
414 359 allMxEntities.Add(lqHytkMxEntity);
415 360  
... ... @@ -426,7 +371,7 @@ namespace NCC.Extend.LqHytkHytk
426 371 Jksxm = ijks_tem.jksxm,
427 372 Jkszh = ijks_tem.jkszh,
428 373 Jksyj = ijks_tem.jksyj,
429   - Tksj = DateTime.Now,
  374 + Tksj = input.tksj,
430 375 F_jsjid = ijks_tem.F_jsjid,
431 376 F_tkpxid = ijks_tem.F_tkpxid,
432 377 F_LaborCost = ijks_tem.F_LaborCost,
... ... @@ -438,7 +383,8 @@ namespace NCC.Extend.LqHytkHytk
438 383 ItemCategory = lqHytkMxEntity.ItemCategory,
439 384 ItemId = lqHytkMxEntity.Px,
440 385 StoreId = newEntity.Md,
441   - ItemName = lqHytkMxEntity.Pxmc
  386 + ItemName = lqHytkMxEntity.Pxmc,
  387 + PerformanceType = lqHytkMxEntity.PerformanceType
442 388 }
443 389 );
444 390 }
... ... @@ -458,7 +404,7 @@ namespace NCC.Extend.LqHytkHytk
458 404 Kjblsxm = ikjbs_tem.kjblsxm,
459 405 Kjblszh = ikjbs_tem.kjblszh,
460 406 Kjblsyj = ikjbs_tem.kjblsyj,
461   - Tksj = DateTime.Now,
  407 + Tksj = input.tksj,
462 408 F_tkpxid = ikjbs_tem.F_tkpxid,
463 409 F_LaborCost = ikjbs_tem.F_LaborCost,
464 410 F_tkpxNumber = ikjbs_tem.F_tkpxNumber,
... ... @@ -469,14 +415,14 @@ namespace NCC.Extend.LqHytkHytk
469 415 ItemCategory = lqHytkMxEntity.ItemCategory,
470 416 ItemId = lqHytkMxEntity.Px,
471 417 StoreId = newEntity.Md,
472   - ItemName = lqHytkMxEntity.Pxmc
  418 + ItemName = lqHytkMxEntity.Pxmc,
  419 + PerformanceType = lqHytkMxEntity.PerformanceType
473 420 }
474 421 );
475 422 }
476 423 }
477 424 }
478 425 }
479   -
480 426 // 批量插入品项明细
481 427 if (allMxEntities.Any())
482 428 {
... ... @@ -570,6 +516,7 @@ namespace NCC.Extend.LqHytkHytk
570 516 SourceType = item.F_SourceType,
571 517 TotalPrice = item.F_TotalPrice ?? (item.pxjg * (item.F_ProjectNumber ?? 1)),
572 518 ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
  519 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "",
573 520 };
574 521 allMxEntities.Add(lqHytkMxEntity);
575 522  
... ... @@ -587,7 +534,7 @@ namespace NCC.Extend.LqHytkHytk
587 534 Jksxm = ijks_tem.jksxm,
588 535 Jkszh = ijks_tem.jkszh,
589 536 Jksyj = ijks_tem.jksyj,
590   - Tksj = DateTime.Now,
  537 + Tksj = input.tksj,
591 538 F_jsjid = ijks_tem.F_jsjid,
592 539 F_tkpxid = ijks_tem.F_tkpxid,
593 540 F_LaborCost = ijks_tem.F_LaborCost,
... ... @@ -597,7 +544,8 @@ namespace NCC.Extend.LqHytkHytk
597 544 ItemCategory = lqHytkMxEntity.ItemCategory,
598 545 ItemId = lqHytkMxEntity.Px,
599 546 StoreId = entity.Md,
600   - ItemName = lqHytkMxEntity.Pxmc
  547 + ItemName = lqHytkMxEntity.Pxmc,
  548 + PerformanceType = lqHytkMxEntity.PerformanceType
601 549 }
602 550 );
603 551 }
... ... @@ -617,7 +565,7 @@ namespace NCC.Extend.LqHytkHytk
617 565 Kjblsxm = ikjbs_tem.kjblsxm,
618 566 Kjblszh = ikjbs_tem.kjblszh,
619 567 Kjblsyj = ikjbs_tem.kjblsyj,
620   - Tksj = DateTime.Now,
  568 + Tksj = input.tksj,
621 569 F_tkpxid = ikjbs_tem.F_tkpxid,
622 570 F_LaborCost = ikjbs_tem.F_LaborCost,
623 571 F_tkpxNumber = ikjbs_tem.F_tkpxNumber,
... ... @@ -626,7 +574,8 @@ namespace NCC.Extend.LqHytkHytk
626 574 ItemCategory = lqHytkMxEntity.ItemCategory,
627 575 ItemId = lqHytkMxEntity.Px,
628 576 StoreId = entity.Md,
629   - ItemName = lqHytkMxEntity.Pxmc
  577 + ItemName = lqHytkMxEntity.Pxmc,
  578 + PerformanceType = lqHytkMxEntity.PerformanceType
630 579 }
631 580 );
632 581 }
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
... ... @@ -47,6 +47,7 @@ using NCC.Extend.Entitys.lq_mdxx;
47 47 using NCC.Extend.Entitys.lq_card_transfer_log;
48 48 using NCC.Extend.Entitys.Dto.LqDailyReport;
49 49 using NCC.Extend.Entitys.lq_yyjl;
  50 +using NCC.Extend.Entitys.lq_hzf;
50 51  
51 52 namespace NCC.Extend.LqKdKdjlb
52 53 {
... ... @@ -931,6 +932,7 @@ namespace NCC.Extend.LqKdKdjlb
931 932 IsEffective = StatusEnum.有效.GetHashCode(),
932 933 ActivityId = input.activityId,
933 934 ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
  935 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "",
934 936 };
935 937 allPxmxEntities.Add(lqKdPxmxEntity);
936 938 // 收集该品项关联的健康师业绩
... ... @@ -956,7 +958,8 @@ namespace NCC.Extend.LqKdKdjlb
956 958 ItemCategory = lqKdPxmxEntity.ItemCategory,
957 959 ItemId = lqKdPxmxEntity.Px,
958 960 ItemName = lqKdPxmxEntity.Pxmc,
959   - StoreId = entity.Djmd
  961 + StoreId = entity.Djmd,
  962 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "",
960 963 });
961 964 }
962 965 }
... ... @@ -981,7 +984,8 @@ namespace NCC.Extend.LqKdKdjlb
981 984 ItemCategory = lqKdPxmxEntity.ItemCategory,
982 985 ItemId = lqKdPxmxEntity.Px,
983 986 ItemName = lqKdPxmxEntity.Pxmc,
984   - StoreId = entity.Djmd
  987 + StoreId = entity.Djmd,
  988 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "",
985 989 }
986 990 );
987 991 }
... ... @@ -1117,6 +1121,266 @@ namespace NCC.Extend.LqKdKdjlb
1117 1121 }
1118 1122 #endregion
1119 1123  
  1124 + #region 修改开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
  1125 + /// <summary>
  1126 + /// 修改开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
  1127 + /// </summary>
  1128 + /// <remarks>
  1129 + /// 参数说明:
  1130 + /// - id: 开单记录主键ID
  1131 + /// - input: 开单记录更新参数,包含品项明细和业绩信息
  1132 + /// </remarks>
  1133 + /// <param name="id">开单记录主键ID</param>
  1134 + /// <param name="input">开单记录更新参数</param>
  1135 + /// <returns>无返回值</returns>
  1136 + /// <response code="200">更新成功</response>
  1137 + /// <response code="400">参数错误或数据验证失败</response>
  1138 + /// <response code="500">服务器内部错误</response>
  1139 + [HttpPut("UpdateForNoDelete/{id}")]
  1140 + public async Task UpdateForNoDelete(string id, [FromBody] LqKdKdjlbUpInput input)
  1141 + {
  1142 + var entity = input.Adapt<LqKdKdjlbEntity>();
  1143 + entity.Id = id; // 确保ID正确设置
  1144 + try
  1145 + {
  1146 + //检查开单记录是否可以操作
  1147 + var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(id);
  1148 + if (!canCancel)
  1149 + {
  1150 + throw NCCException.Oh(errorMessage);
  1151 + }
  1152 + //开启事务
  1153 + _db.BeginTran();
  1154 + //查询是否有对应的补缴开单ID
  1155 + if (!string.IsNullOrEmpty(entity.SupplementBillingId))
  1156 + {
  1157 + //查询补缴开单ID
  1158 + var supplementBillingEntity = await _db.Queryable<LqKdKdjlbEntity>().FirstAsync(p => p.Id == entity.SupplementBillingId);//900,900
  1159 + if (supplementBillingEntity == null || supplementBillingEntity.IsEffective == StatusEnum.无效.GetHashCode())
  1160 + {
  1161 + throw NCCException.Oh("补缴开单记录不存在或已作废");
  1162 + }
  1163 + //查询当前开单已经补缴金额
  1164 + var OldSupplementAmount = await _db.Queryable<LqKdKdjlbEntity>().Where(p => p.Id == id).SumAsync(p => p.SupplementAmount);//900,0
  1165 + supplementBillingEntity.PaidDebt = supplementBillingEntity.PaidDebt - OldSupplementAmount + input.supplementAmount;
  1166 + await _db.Updateable(supplementBillingEntity).ExecuteCommandAsync();
  1167 + }
  1168 + //批量查询当前开单所有品项的分类(性能优化)
  1169 + var itemIds = input.lqKdPxmxList?.Where(x => !string.IsNullOrEmpty(x.px)).Select(x => x.px).Distinct().ToList() ?? new List<string>();
  1170 + var itemCategoryDict = new Dictionary<string, string>();
  1171 + if (itemIds.Any())
  1172 + {
  1173 + var itemCategories = await _db.Queryable<LqXmzlEntity>()
  1174 + .Where(x => itemIds.Contains(x.Id) && x.IsEffective == StatusEnum.有效.GetHashCode())
  1175 + .Select(x => new { x.Id, x.Qt2 })
  1176 + .ToListAsync();
  1177 + itemCategoryDict = itemCategories.ToDictionary(k => k.Id, v => v.Qt2 ?? "");
  1178 + }
  1179 + //判断当前开单是否包含医美品项
  1180 + var hasMedicalItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美");
  1181 + var MedicalItemInCurrentBillingAmount = input.lqKdPxmxList.Where(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美").Sum(x => x.actualPrice);
  1182 + //判断当前开单是否包含科美品项
  1183 + var hasTechItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "科美");
  1184 + //判断当前开单是否包含生美品项
  1185 + var hasLifeItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x => !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "生美");
  1186 + //获取该会员之前开单品项里面是否有医美项目
  1187 + var isMedicalProject = hasMedicalItemInCurrentBilling && await _db.Queryable<LqKdPxmxEntity>().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "医美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
  1188 + if (isMedicalProject && MedicalItemInCurrentBillingAmount >= 1000)
  1189 + {
  1190 + entity.UpgradeLifeBeauty = "是";
  1191 + }
  1192 + else
  1193 + {
  1194 + entity.UpgradeLifeBeauty = "否";
  1195 + }
  1196 + //获取该会员之前开单品项里面是否有科美项目
  1197 + var isTechProject = hasTechItemInCurrentBilling && await _db.Queryable<LqKdPxmxEntity>().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "科美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
  1198 + if (isTechProject)
  1199 + {
  1200 + entity.UpgradeTechBeauty = "是";
  1201 + }
  1202 + else
  1203 + {
  1204 + entity.UpgradeTechBeauty = "否";
  1205 + }
  1206 + //获取该会员之前开单品项里面是否有生美项目
  1207 + var isLifeProject = hasLifeItemInCurrentBilling && await _db.Queryable<LqKdPxmxEntity>().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "生美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
  1208 + if (isLifeProject)
  1209 + {
  1210 + entity.UpgradeMedicalBeauty = "是";
  1211 + }
  1212 + else
  1213 + {
  1214 + entity.UpgradeMedicalBeauty = "否";
  1215 + }
  1216 + //计算储扣总金额
  1217 + entity.DeductAmount = input.lqKdKdjlbDeductList.Sum(x => x.Amount ?? 0);
  1218 + entity.UpdateTime = DateTime.Now;
  1219 + // 更新开单记录主表
  1220 + await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).IgnoreColumns(x => x.CreateTime).ExecuteCommandAsync();
  1221 + //清空原有品项明细
  1222 + await _db.Deleteable<LqKdPxmxEntity>().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
  1223 + //清空原有健康师业绩
  1224 + await _db.Deleteable<LqKdJksyjEntity>().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
  1225 + //清空原有科技部老师业绩
  1226 + await _db.Deleteable<LqKdKjbsyjEntity>().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
  1227 + //清空原有扣款信息
  1228 + await _db.Deleteable<LqKdDeductinfoEntity>().Where(x => x.BillingId == id).ExecuteCommandAsync();
  1229 + //循环品相信息
  1230 + // 收集所有需要插入的实体,然后批量插入
  1231 + var allPxmxEntities = new List<LqKdPxmxEntity>();
  1232 + var allJksyjEntities = new List<LqKdJksyjEntity>();
  1233 + var allKjbsyjEntities = new List<LqKdKjbsyjEntity>();
  1234 + var allDeductEntities = new List<LqKdDeductinfoEntity>();
  1235 + // 处理扣款信息列表
  1236 + foreach (var item in input.lqKdKdjlbDeductList)
  1237 + {
  1238 + var lqKdDeductEntity = new LqKdDeductinfoEntity
  1239 + {
  1240 + Id = YitIdHelper.NextId().ToString(),
  1241 + BillingId = id,
  1242 + DeductId = item.DeductId,
  1243 + DeductType = item.DeductType,
  1244 + Amount = item.Amount,
  1245 + ProjectNumber = item.ProjectNumber,
  1246 + UnitPrice = item.UnitPrice,
  1247 + ItemName = item.ItemName,
  1248 + ItemId = item.ItemId,
  1249 + IsEffective = StatusEnum.有效.GetHashCode(), // 设置为有效
  1250 + CreateTime = DateTime.Now, // 设置创建时间
  1251 + ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync(),
  1252 + };
  1253 + allDeductEntities.Add(lqKdDeductEntity);
  1254 + }
  1255 + // 处理品项明细列表
  1256 + foreach (var item in input.lqKdPxmxList)
  1257 + {
  1258 + // 创建品项明细实体
  1259 + var lqKdPxmxEntity = new LqKdPxmxEntity
  1260 + {
  1261 + Id = YitIdHelper.NextId().ToString(),
  1262 + Glkdbh = id,
  1263 + Yjsj = input.kdrq,
  1264 + CreateTIme = DateTime.Now,
  1265 + MemberId = entity.Kdhy,
  1266 + IsEnabled = StatusEnum.有效.GetHashCode(),
  1267 + ProjectNumber = item.projectNumber,
  1268 + TotalPrice = (decimal)(item.pxjg * item.projectNumber),
  1269 + Px = item.px,
  1270 + Pxmc = item.pxmc,
  1271 + Pxjg = item.pxjg,
  1272 + SourceType = item.sourceType,
  1273 + ActualPrice = item.actualPrice,
  1274 + Remark = item.remark,
  1275 + IsEffective = StatusEnum.有效.GetHashCode(),
  1276 + ActivityId = input.activityId,
  1277 + ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
  1278 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "",
  1279 + };
  1280 + allPxmxEntities.Add(lqKdPxmxEntity);
  1281 +
  1282 + // 收集该品项关联的健康师业绩
  1283 + if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any())
  1284 + {
  1285 + //把jksxm保存到HealthInstructorNames
  1286 + foreach (var ijks_tem in item.lqKdJksyjList)
  1287 + {
  1288 + allJksyjEntities.Add(new LqKdJksyjEntity
  1289 + {
  1290 + Id = YitIdHelper.NextId().ToString(),
  1291 + Glkdbh = id,
  1292 + Jks = ijks_tem.jks,
  1293 + Jksxm = ijks_tem.jksxm,
  1294 + Jkszh = ijks_tem.jkszh,
  1295 + Jksyj = ijks_tem.jksyj,
  1296 + Yjsj = input.kdrq,
  1297 + Jsj_id = ijks_tem.jsj_id,
  1298 + Kdpxid = lqKdPxmxEntity.Id,
  1299 + IsEffective = StatusEnum.有效.GetHashCode(),
  1300 + ActivityId = input.activityId,
  1301 + ItemCategory = lqKdPxmxEntity.ItemCategory,
  1302 + ItemId = lqKdPxmxEntity.Px,
  1303 + StoreId = entity.Djmd,
  1304 + ItemName = lqKdPxmxEntity.Pxmc,
  1305 + PerformanceType = lqKdPxmxEntity.PerformanceType,
  1306 + });
  1307 + }
  1308 + }
  1309 +
  1310 + // 收集该品项关联的科技部老师业绩
  1311 + if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any())
  1312 + {
  1313 + foreach (var ikjbs_tem in item.lqKdKjbsyjList)
  1314 + {
  1315 + allKjbsyjEntities.Add(new LqKdKjbsyjEntity
  1316 + {
  1317 + Id = YitIdHelper.NextId().ToString(),
  1318 + Glkdbh = id,
  1319 + Kjbls = ikjbs_tem.kjbls,
  1320 + Kjblsxm = ikjbs_tem.kjblsxm,
  1321 + Kjblszh = ikjbs_tem.kjblszh,
  1322 + Kjblsyj = ikjbs_tem.kjblsyj,
  1323 + Yjsj = input.kdrq,
  1324 + Kdpxid = lqKdPxmxEntity.Id,
  1325 + IsEffective = StatusEnum.有效.GetHashCode(),
  1326 + ActivityId = input.activityId,
  1327 + ItemCategory = lqKdPxmxEntity.ItemCategory,
  1328 + ItemId = lqKdPxmxEntity.Px,
  1329 + StoreId = entity.Djmd,
  1330 + ItemName = lqKdPxmxEntity.Pxmc,
  1331 + PerformanceType = lqKdPxmxEntity.PerformanceType,
  1332 + });
  1333 + }
  1334 + }
  1335 + }
  1336 +
  1337 + //通过会员id查询会员信息
  1338 + var memberInfo = await _db.Queryable<LqKhxxEntity>().Where(u => u.Id == entity.Kdhy).FirstAsync();
  1339 + //通过开单记录表查询这个会员开单金额
  1340 + var kdAmount = await _db.Queryable<LqKdKdjlbEntity>().Where(u => u.Kdhy == entity.Kdhy).SumAsync(u => u.Sfyj);
  1341 + //如果开单金额小于500,为散客,如果大于500,为会员
  1342 + if (kdAmount < 500)
  1343 + {
  1344 + memberInfo.Khlx = MemberTypeEnum.散客.GetHashCode().ToString();
  1345 + }
  1346 + else
  1347 + {
  1348 + memberInfo.Khlx = MemberTypeEnum.会员.GetHashCode().ToString();
  1349 + }
  1350 + await _db.Updateable(memberInfo).ExecuteCommandAsync();
  1351 + // 批量插入扣款信息
  1352 + if (allDeductEntities.Any())
  1353 + {
  1354 + await _db.Insertable(allDeductEntities).ExecuteCommandAsync();
  1355 + }
  1356 + // 批量插入品项明细
  1357 + if (allPxmxEntities.Any())
  1358 + {
  1359 + await _db.Insertable(allPxmxEntities).ExecuteCommandAsync();
  1360 + }
  1361 + // 批量插入健康师业绩
  1362 + if (allJksyjEntities.Any())
  1363 + {
  1364 + await _db.Insertable(allJksyjEntities).ExecuteCommandAsync();
  1365 + }
  1366 + // 批量插入科技部老师业绩
  1367 + if (allKjbsyjEntities.Any())
  1368 + {
  1369 + await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync();
  1370 + }
  1371 +
  1372 + //关闭事务
  1373 + _db.CommitTran();
  1374 + }
  1375 + catch (Exception ex)
  1376 + {
  1377 + //回滚事务
  1378 + _db.RollbackTran();
  1379 + throw NCCException.Oh($"创建开单记录失败: {ex.Message}");
  1380 + }
  1381 + }
  1382 + #endregion
  1383 +
1120 1384 #region 获取开单记录表无分页列表
1121 1385 /// <summary>
1122 1386 /// 获取开单记录表无分页列表
... ... @@ -1256,581 +1520,93 @@ namespace NCC.Extend.LqKdKdjlb
1256 1520 var consumeRecords = await _db.Queryable<LqXhPxmxEntity>().Where(x => x.IsEffective == StatusEnum.有效.GetHashCode()).Where(x => billingItemIds.Contains(x.BillingItemId)).AnyAsync();
1257 1521 if (consumeRecords)
1258 1522 {
1259   - throw NCCException.Oh("选中的开单记录中有已消耗的记录,不能删除");
1260   - }
1261   - }
1262   - // 检查是否有储扣记录
1263   - var deductRecords = await _db.Queryable<LqKdDeductinfoEntity>().Where(x => ids.Contains(x.BillingId) && x.IsEffective == StatusEnum.有效.GetHashCode()).AnyAsync();
1264   - if (deductRecords)
1265   - {
1266   - throw NCCException.Oh("选中的开单记录中有储扣记录,不能删除");
1267   - }
1268   - //批量删除开单记录表
1269   - await _db.Deleteable<LqKdKdjlbEntity>().In(d => d.Id, ids).ExecuteCommandAsync();
1270   -
1271   - //清空健康师业绩
1272   - await _db.Deleteable<LqKdJksyjEntity>().In(u => u.Glkdbh, ids).ExecuteCommandAsync();
1273   -
1274   - //清空科技部老师业绩
1275   - await _db.Deleteable<LqKdKjbsyjEntity>().In(u => u.Glkdbh, ids).ExecuteCommandAsync();
1276   -
1277   - //清空品项明细
1278   - await _db.Deleteable<LqKdPxmxEntity>().In(u => u.Glkdbh, ids).ExecuteCommandAsync();
1279   -
1280   - //清空扣款信息
1281   - await _db.Deleteable<LqKdDeductinfoEntity>().In(u => u.BillingId, ids).ExecuteCommandAsync();
1282   -
1283   - //关闭事务
1284   - _db.CommitTran();
1285   - }
1286   - catch (Exception ex)
1287   - {
1288   - //回滚事务
1289   - _db.RollbackTran();
1290   - throw NCCException.Oh($"批量删除开单记录表失败: {ex.Message}");
1291   - }
1292   - }
1293   - }
1294   - #endregion
1295   -
1296   - #region 更新开单记录表
1297   - /// <summary>
1298   - /// 更新开单记录表
1299   - /// </summary>
1300   - /// <remarks>
1301   - /// 更新开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
1302   - ///
1303   - /// 示例请求:
1304   - /// ```json
1305   - /// {
1306   - /// "id": "开单编号",
1307   - /// "djmd": "单据门店",
1308   - /// "jsj": "金三角",
1309   - /// "kdrq": "2025-01-11",
1310   - /// "lqKdPxmxList": [
1311   - /// {
1312   - /// "px": "品项编号",
1313   - /// "pxmc": "品项名称",
1314   - /// "pxjg": 100.00,
1315   - /// "projectNumber": 1,
1316   - /// "sourceType": "购买",
1317   - /// "lqKdJksyjList": [
1318   - /// {
1319   - /// "jks": "健康师",
1320   - /// "jksxm": "健康师姓名",
1321   - /// "jksyj": "100"
1322   - /// }
1323   - /// ]
1324   - /// }
1325   - /// ]
1326   - /// }
1327   - /// ```
1328   - ///
1329   - /// 参数说明:
1330   - /// - id: 开单记录主键ID
1331   - /// - input: 开单记录更新参数,包含品项明细和业绩信息
1332   - /// </remarks>
1333   - /// <param name="id">开单记录主键ID</param>
1334   - /// <param name="input">开单记录更新参数</param>
1335   - /// <returns>无返回值</returns>
1336   - /// <response code="200">更新成功</response>
1337   - /// <response code="400">参数错误或数据验证失败</response>
1338   - /// <response code="500">服务器内部错误</response>
1339   - [HttpPut("{id}")]
1340   - public async Task Update(string id, [FromBody] LqKdKdjlbUpInput input)
1341   - {
1342   - var entity = input.Adapt<LqKdKdjlbEntity>();
1343   - try
1344   - {
1345   - //开启事务
1346   - _db.BeginTran();
1347   - //批量查询当前开单所有品项的分类(性能优化)
1348   - var itemIds = input.lqKdPxmxList?.Where(x => !string.IsNullOrEmpty(x.px)).Select(x => x.px).Distinct().ToList() ?? new List<string>();
1349   - var itemCategoryDict = new Dictionary<string, string>();
1350   - if (itemIds.Any())
1351   - {
1352   - var itemCategories = await _db.Queryable<LqXmzlEntity>()
1353   - .Where(x => itemIds.Contains(x.Id) && x.IsEffective == StatusEnum.有效.GetHashCode())
1354   - .Select(x => new { x.Id, x.Qt2 })
1355   - .ToListAsync();
1356   - itemCategoryDict = itemCategories.ToDictionary(k => k.Id, v => v.Qt2 ?? "");
1357   - }
1358   - //判断当前开单是否包含医美品项
1359   - var hasMedicalItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x =>
1360   - !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美");
1361   - var MedicalItemInCurrentBillingAmount = input.lqKdPxmxList.Where(x =>
1362   - !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "医美").Sum(x => x.actualPrice);
1363   - //判断当前开单是否包含科美品项
1364   - var hasTechItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x =>
1365   - !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "科美");
1366   - //判断当前开单是否包含生美品项
1367   - var hasLifeItemInCurrentBilling = input.lqKdPxmxList != null && input.lqKdPxmxList.Any(x =>
1368   - !string.IsNullOrEmpty(x.px) && itemCategoryDict.ContainsKey(x.px) && itemCategoryDict[x.px] == "生美");
1369   - //获取该会员之前开单品项里面是否有医美项目
1370   - var isMedicalProject = hasMedicalItemInCurrentBilling && await _db.Queryable<LqKdPxmxEntity>().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "医美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
1371   - if (isMedicalProject && MedicalItemInCurrentBillingAmount >= 1000)
1372   - {
1373   - entity.UpgradeLifeBeauty = "是";
1374   - }
1375   - else
1376   - {
1377   - entity.UpgradeLifeBeauty = "否";
1378   - }
1379   - //获取该会员之前开单品项里面是否有科美项目
1380   - var isTechProject = hasTechItemInCurrentBilling && await _db.Queryable<LqKdPxmxEntity>().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "科美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
1381   - if (isTechProject)
1382   - {
1383   - entity.UpgradeTechBeauty = "是";
1384   - }
1385   - else
1386   - {
1387   - entity.UpgradeTechBeauty = "否";
1388   - }
1389   - //获取该会员之前开单品项里面是否有生美项目
1390   - var isLifeProject = hasLifeItemInCurrentBilling && await _db.Queryable<LqKdPxmxEntity>().Where(x => x.MemberId == entity.Kdhy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.ItemCategory == "生美" && x.ActualPrice > 0 && x.Px != "61" && x.Glkdbh != id).AnyAsync();
1391   - if (isLifeProject)
1392   - {
1393   - entity.UpgradeMedicalBeauty = "是";
1394   - }
1395   - else
1396   - {
1397   - entity.UpgradeMedicalBeauty = "否";
1398   - }
1399   -
1400   - //更新开单记录表记录
1401   - await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
1402   -
1403   - //清空原有数据
1404   - await _db.Deleteable<LqKdJksyjEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
1405   - await _db.Deleteable<LqKdKjbsyjEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
1406   - await _db.Deleteable<LqKdPxmxEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
1407   -
1408   - // 收集所有需要插入的实体,然后批量插入
1409   - var allPxmxEntities = new List<LqKdPxmxEntity>();
1410   - var allJksyjEntities = new List<LqKdJksyjEntity>();
1411   - var allKjbsyjEntities = new List<LqKdKjbsyjEntity>();
1412   -
1413   - // 处理品项明细列表
1414   - if (input.lqKdPxmxList != null && input.lqKdPxmxList.Any())
1415   - {
1416   - foreach (var item in input.lqKdPxmxList)
1417   - {
1418   - // 创建品项明细实体
1419   - var lqKdPxmxEntity = new LqKdPxmxEntity
1420   - {
1421   - Id = YitIdHelper.NextId().ToString(),
1422   - Glkdbh = entity.Id,
1423   - CreateTIme = DateTime.Now,
1424   - MemberId = entity.Kdhy,
1425   - IsEnabled = 0,
1426   - ProjectNumber = item.projectNumber == 0 ? 1 : item.projectNumber,
1427   - TotalPrice = (decimal)(item.pxjg * (item.projectNumber == 0 ? 1 : item.projectNumber)),
1428   - Px = item.px,
1429   - Pxmc = item.pxmc,
1430   - Pxjg = item.pxjg,
1431   - SourceType = item.sourceType,
1432   - ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
1433   - };
1434   - allPxmxEntities.Add(lqKdPxmxEntity);
1435   -
1436   - // 收集该品项关联的健康师业绩
1437   - if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any())
1438   - {
1439   - foreach (var ijks_tem in item.lqKdJksyjList)
1440   - {
1441   - allJksyjEntities.Add(new LqKdJksyjEntity
1442   - {
1443   - Id = YitIdHelper.NextId().ToString(),
1444   - Glkdbh = entity.Id,
1445   - Jks = ijks_tem.jks,
1446   - Jksxm = ijks_tem.jksxm,
1447   - Jkszh = ijks_tem.jkszh,
1448   - Jksyj = ijks_tem.jksyj,
1449   - Yjsj = DateTime.Now,
1450   - Jsj_id = ijks_tem.jsj_id,
1451   - Kdpxid = lqKdPxmxEntity.Id,
1452   - StoreId = entity.Djmd,
1453   - ItemCategory = lqKdPxmxEntity.ItemCategory,
1454   - ItemId = lqKdPxmxEntity.Px,
1455   - ItemName = lqKdPxmxEntity.Pxmc,
1456   - });
1457   - }
1458   - }
1459   -
1460   - // 收集该品项关联的科技部老师业绩
1461   - if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any())
1462   - {
1463   - foreach (var ikjbs_tem in item.lqKdKjbsyjList)
1464   - {
1465   - allKjbsyjEntities.Add(new LqKdKjbsyjEntity
1466   - {
1467   - Id = YitIdHelper.NextId().ToString(),
1468   - Glkdbh = entity.Id,
1469   - Kjbls = ikjbs_tem.kjbls,
1470   - Kjblsxm = ikjbs_tem.kjblsxm,
1471   - Kjblszh = ikjbs_tem.kjblszh,
1472   - Kjblsyj = ikjbs_tem.kjblsyj,
1473   - Yjsj = DateTime.Now,
1474   - Kdpxid = lqKdPxmxEntity.Id,
1475   - ItemCategory = lqKdPxmxEntity.ItemCategory,
1476   - ItemId = lqKdPxmxEntity.Px,
1477   - StoreId = entity.Djmd,
1478   - ItemName = lqKdPxmxEntity.Pxmc,
1479   - });
1480   - }
1481   - }
1482   - }
1483   - }
1484   -
1485   - // 批量插入品项明细
1486   - if (allPxmxEntities.Any())
1487   - {
1488   - await _db.Insertable(allPxmxEntities).ExecuteCommandAsync();
1489   - }
1490   - // 批量插入健康师业绩
1491   - if (allJksyjEntities.Any())
1492   - {
1493   - await _db.Insertable(allJksyjEntities).ExecuteCommandAsync();
1494   - }
1495   - // 批量插入科技部老师业绩
1496   - if (allKjbsyjEntities.Any())
1497   - {
1498   - await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync();
1499   - }
1500   - //关闭事务
1501   - _db.CommitTran();
1502   - }
1503   - catch (Exception)
1504   - {
1505   - //回滚事务
1506   - _db.RollbackTran();
1507   - throw NCCException.Oh(ErrorCode.COM1001);
1508   - }
1509   - }
1510   - #endregion
1511   -
1512   - #region 删除开单记录表
1513   - /// <summary>
1514   - /// 删除开单记录表
1515   - /// </summary>
1516   - /// <returns></returns>
1517   - [HttpDelete("{id}")]
1518   - public async Task Delete(string id)
1519   - {
1520   - var entity = await _db.Queryable<LqKdKdjlbEntity>().FirstAsync(p => p.Id == id);
1521   - _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
1522   - try
1523   - {
1524   - //开启事务
1525   - _db.BeginTran();
1526   - //先查询开单品项明细
1527   - var lqPxmxList = _db.Queryable<LqKdPxmxEntity>().Where(x => x.Glkdbh == id).ToList();
1528   - //判断是否有对应的消耗记录
1529   - var consumeRecords = await _db.Queryable<LqXhPxmxEntity>().Where(x => lqPxmxList.Select(y => y.Id).Contains(x.BillingItemId)).AnyAsync();
1530   - if (consumeRecords)
1531   - {
1532   - throw NCCException.Oh("选中的开单记录中有已消耗的记录,不能删除");
1533   - }
1534   - //判断是否有对应的退卡记录
1535   - var refundRecords = await _db.Queryable<LqHytkMxEntity>().Where(x => lqPxmxList.Select(y => y.Id).Contains(x.BillingItemId)).AnyAsync();
1536   - if (refundRecords)
1537   - {
1538   - throw NCCException.Oh("选中的开单记录中有退卡记录,不能删除");
1539   - }
1540   - //判断是否有对应的储扣记录
1541   - var deductRecords = await _db.Queryable<LqKdDeductinfoEntity>().Where(x => lqPxmxList.Select(y => y.Id).Contains(x.DeductId)).AnyAsync();
1542   - if (deductRecords)
1543   - {
1544   - throw NCCException.Oh("选中的开单记录中有储扣记录,不能删除");
1545   - }
1546   -
1547   - //删除开单记录表记录
1548   - await _db.Deleteable<LqKdKdjlbEntity>().Where(d => d.Id == id).ExecuteCommandAsync();
1549   -
1550   - //清空健康师业绩
1551   - await _db.Deleteable<LqKdJksyjEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
1552   -
1553   - //清空科技部老师业绩
1554   - await _db.Deleteable<LqKdKjbsyjEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
1555   -
1556   - //清空品项明细
1557   - await _db.Deleteable<LqKdPxmxEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
1558   -
1559   - //清空扣款信息
1560   - await _db.Deleteable<LqKdDeductinfoEntity>().Where(u => u.BillingId == id).ExecuteCommandAsync();
1561   -
1562   - //关闭事务
1563   - _db.CommitTran();
1564   - }
1565   - catch (Exception ex)
1566   - {
1567   - //回滚事务
1568   - _db.RollbackTran();
1569   - throw NCCException.Oh($"删除开单记录表失败: {ex.Message}");
1570   - }
1571   - }
1572   - #endregion
1573   -
1574   - #region 获取扣款类型枚举内容
1575   - /// <summary>
1576   - /// 获取扣款类型枚举内容
1577   - /// </summary>
1578   - /// <returns>扣款类型枚举列表</returns>
1579   - [HttpGet("deduct-types")]
1580   - public List<EnumOutput> GetDeductTypes()
1581   - {
1582   - return Enum.GetValues<DeductTypeEnum>()
1583   - .Select(e => new EnumOutput
1584   - {
1585   - Value = (int)e,
1586   - Name = e.ToString(),
1587   - Description = e.GetDescription(),
1588   - })
1589   - .ToList();
1590   - }
1591   - #endregion
1592   -
1593   - #region 获取状态枚举内容(所有的状态通用)
1594   - /// <summary>
1595   - /// 获取状态枚举内容
1596   - /// </summary>
1597   - /// <returns>状态枚举列表</returns>
1598   - [HttpGet("status-enum")]
1599   - public List<EnumOutput> GetStatusEnum()
1600   - {
1601   - return Enum.GetValues<StatusEnum>()
1602   - .Select(e => new EnumOutput
1603   - {
1604   - Value = (int)e,
1605   - Name = e.ToString(),
1606   - Description = e.GetDescription(),
1607   - })
1608   - .ToList();
1609   - }
1610   - #endregion
1611   -
1612   - #region 修改开单记录
1613   - /// <summary>
1614   - /// 修改开单记录
1615   - /// </summary>
1616   - /// <remarks>
1617   - /// 更新开单记录及其关联的品项明细、健康师业绩、科技部老师业绩信息
1618   - ///
1619   - /// 示例请求:
1620   - /// ```json
1621   - /// {
1622   - /// "id": "开单编号",
1623   - /// "djmd": "单据门店",
1624   - /// "jsj": "金三角",
1625   - /// "kdrq": "2025-01-11",
1626   - /// "lqKdPxmxList": [
1627   - /// {
1628   - /// "px": "品项编号",
1629   - /// "pxmc": "品项名称",
1630   - /// "pxjg": 100.00,
1631   - /// "projectNumber": 1,
1632   - /// "sourceType": "购买",
1633   - /// "lqKdJksyjList": [
1634   - /// {
1635   - /// "jks": "健康师",
1636   - /// "jksxm": "健康师姓名",
1637   - /// "jksyj": "100"
1638   - /// }
1639   - /// ]
1640   - /// }
1641   - /// ]
1642   - /// }
1643   - /// ```
1644   - ///
1645   - /// 参数说明:
1646   - /// - id: 开单记录主键ID
1647   - /// - input: 开单记录更新参数,包含品项明细和业绩信息
1648   - /// </remarks>
1649   - /// <param name="id">开单记录主键ID</param>
1650   - /// <param name="input">开单记录更新参数</param>
1651   - /// <returns>无返回值</returns>
1652   - /// <response code="200">更新成功</response>
1653   - /// <response code="400">参数错误或数据验证失败</response>
1654   - /// <response code="500">服务器内部错误</response>
1655   - [HttpPut("UpdateForNoDelete/{id}")]
1656   - public async Task UpdateForNoDelete(string id, [FromBody] LqKdKdjlbUpInput input)
1657   - {
1658   - var entity = input.Adapt<LqKdKdjlbEntity>();
1659   - entity.Id = id; // 确保ID正确设置
1660   - try
1661   - {
1662   - //检查开单记录是否可以操作
1663   - var (canCancel, errorMessage) = await CheckBillingCanCancelAsync(id);
1664   - if (!canCancel)
1665   - {
1666   - throw NCCException.Oh(errorMessage);
1667   - }
1668   - //开启事务
1669   - _db.BeginTran();
1670   - //查询是否有对应的补缴开单ID
1671   - if (!string.IsNullOrEmpty(entity.SupplementBillingId))
1672   - {
1673   - //查询补缴开单ID
1674   - var supplementBillingEntity = await _db.Queryable<LqKdKdjlbEntity>().FirstAsync(p => p.Id == entity.SupplementBillingId);//900,900
1675   - if (supplementBillingEntity == null || supplementBillingEntity.IsEffective == StatusEnum.无效.GetHashCode())
1676   - {
1677   - throw NCCException.Oh("补缴开单记录不存在或已作废");
1678   - }
1679   - //查询当前开单已经补缴金额
1680   - var OldSupplementAmount = await _db.Queryable<LqKdKdjlbEntity>().Where(p => p.Id == id).SumAsync(p => p.SupplementAmount);//900,0
1681   - supplementBillingEntity.PaidDebt = supplementBillingEntity.PaidDebt - OldSupplementAmount + input.supplementAmount;
1682   - await _db.Updateable(supplementBillingEntity).ExecuteCommandAsync();
1683   - }
1684   - // 更新开单记录主表
1685   - await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).IgnoreColumns(x => x.CreateTime).ExecuteCommandAsync();
1686   -
1687   - //清空原有品项明细
1688   - await _db.Deleteable<LqKdPxmxEntity>().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
1689   - //清空原有健康师业绩
1690   - await _db.Deleteable<LqKdJksyjEntity>().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
1691   - //清空原有科技部老师业绩
1692   - await _db.Deleteable<LqKdKjbsyjEntity>().Where(x => x.Glkdbh == id).ExecuteCommandAsync();
1693   - //清空原有扣款信息
1694   - await _db.Deleteable<LqKdDeductinfoEntity>().Where(x => x.BillingId == id).ExecuteCommandAsync();
1695   - //循环品相信息
1696   - // 收集所有需要插入的实体,然后批量插入
1697   - var allPxmxEntities = new List<LqKdPxmxEntity>();
1698   - var allJksyjEntities = new List<LqKdJksyjEntity>();
1699   - var allKjbsyjEntities = new List<LqKdKjbsyjEntity>();
1700   - var allDeductEntities = new List<LqKdDeductinfoEntity>();
1701   - // 处理扣款信息列表
1702   - foreach (var item in input.lqKdKdjlbDeductList)
1703   - {
1704   - var lqKdDeductEntity = new LqKdDeductinfoEntity
1705   - {
1706   - Id = YitIdHelper.NextId().ToString(),
1707   - BillingId = id,
1708   - DeductId = item.DeductId,
1709   - DeductType = item.DeductType,
1710   - Amount = item.Amount,
1711   - ProjectNumber = item.ProjectNumber,
1712   - UnitPrice = item.UnitPrice,
1713   - ItemName = item.ItemName,
1714   - ItemId = item.ItemId,
1715   - IsEffective = StatusEnum.有效.GetHashCode(), // 设置为有效
1716   - CreateTime = DateTime.Now, // 设置创建时间
1717   - ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.DeductId).Select(x => x.Qt2).FirstAsync(),
1718   - };
1719   - allDeductEntities.Add(lqKdDeductEntity);
1720   - }
1721   - // 处理品项明细列表
1722   - foreach (var item in input.lqKdPxmxList)
1723   - {
1724   - // 创建品项明细实体
1725   - var lqKdPxmxEntity = new LqKdPxmxEntity
1726   - {
1727   - Id = YitIdHelper.NextId().ToString(),
1728   - Glkdbh = id,
1729   - Yjsj = input.kdrq,
1730   - CreateTIme = DateTime.Now,
1731   - MemberId = entity.Kdhy,
1732   - IsEnabled = StatusEnum.有效.GetHashCode(),
1733   - ProjectNumber = item.projectNumber,
1734   - TotalPrice = (decimal)(item.pxjg * item.projectNumber),
1735   - Px = item.px,
1736   - Pxmc = item.pxmc,
1737   - Pxjg = item.pxjg,
1738   - SourceType = item.sourceType,
1739   - ActualPrice = item.actualPrice,
1740   - Remark = item.remark,
1741   - IsEffective = StatusEnum.有效.GetHashCode(),
1742   - ActivityId = input.activityId,
1743   - ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
1744   - };
1745   - allPxmxEntities.Add(lqKdPxmxEntity);
1746   -
1747   - // 收集该品项关联的健康师业绩
1748   - if (item.lqKdJksyjList != null && item.lqKdJksyjList.Any())
1749   - {
1750   - //把jksxm保存到HealthInstructorNames
1751   - foreach (var ijks_tem in item.lqKdJksyjList)
1752   - {
1753   - allJksyjEntities.Add(new LqKdJksyjEntity
1754   - {
1755   - Id = YitIdHelper.NextId().ToString(),
1756   - Glkdbh = id,
1757   - Jks = ijks_tem.jks,
1758   - Jksxm = ijks_tem.jksxm,
1759   - Jkszh = ijks_tem.jkszh,
1760   - Jksyj = ijks_tem.jksyj,
1761   - Yjsj = input.kdrq,
1762   - Jsj_id = ijks_tem.jsj_id,
1763   - Kdpxid = lqKdPxmxEntity.Id,
1764   - IsEffective = StatusEnum.有效.GetHashCode(),
1765   - ActivityId = input.activityId,
1766   - ItemCategory = lqKdPxmxEntity.ItemCategory,
1767   - ItemId = lqKdPxmxEntity.Px,
1768   - });
1769   - }
1770   - }
1771   -
1772   - // 收集该品项关联的科技部老师业绩
1773   - if (item.lqKdKjbsyjList != null && item.lqKdKjbsyjList.Any())
1774   - {
1775   - foreach (var ikjbs_tem in item.lqKdKjbsyjList)
1776   - {
1777   - allKjbsyjEntities.Add(new LqKdKjbsyjEntity
1778   - {
1779   - Id = YitIdHelper.NextId().ToString(),
1780   - Glkdbh = id,
1781   - Kjbls = ikjbs_tem.kjbls,
1782   - Kjblsxm = ikjbs_tem.kjblsxm,
1783   - Kjblszh = ikjbs_tem.kjblszh,
1784   - Kjblsyj = ikjbs_tem.kjblsyj,
1785   - Yjsj = input.kdrq,
1786   - Kdpxid = lqKdPxmxEntity.Id,
1787   - IsEffective = StatusEnum.有效.GetHashCode(),
1788   - ActivityId = input.activityId,
1789   - ItemCategory = lqKdPxmxEntity.ItemCategory,
1790   - ItemId = lqKdPxmxEntity.Px,
1791   - }
1792   - );
  1523 + throw NCCException.Oh("选中的开单记录中有已消耗的记录,不能删除");
1793 1524 }
1794 1525 }
1795   - }
  1526 + // 检查是否有储扣记录
  1527 + var deductRecords = await _db.Queryable<LqKdDeductinfoEntity>().Where(x => ids.Contains(x.BillingId) && x.IsEffective == StatusEnum.有效.GetHashCode()).AnyAsync();
  1528 + if (deductRecords)
  1529 + {
  1530 + throw NCCException.Oh("选中的开单记录中有储扣记录,不能删除");
  1531 + }
  1532 + //批量删除开单记录表
  1533 + await _db.Deleteable<LqKdKdjlbEntity>().In(d => d.Id, ids).ExecuteCommandAsync();
1796 1534  
  1535 + //清空健康师业绩
  1536 + await _db.Deleteable<LqKdJksyjEntity>().In(u => u.Glkdbh, ids).ExecuteCommandAsync();
1797 1537  
  1538 + //清空科技部老师业绩
  1539 + await _db.Deleteable<LqKdKjbsyjEntity>().In(u => u.Glkdbh, ids).ExecuteCommandAsync();
1798 1540  
1799   - //通过会员id查询会员信息
1800   - var memberInfo = await _db.Queryable<LqKhxxEntity>().Where(u => u.Id == entity.Kdhy).FirstAsync();
1801   - //通过开单记录表查询这个会员开单金额
1802   - var kdAmount = await _db.Queryable<LqKdKdjlbEntity>().Where(u => u.Kdhy == entity.Kdhy).SumAsync(u => u.Sfyj);
1803   - //如果开单金额小于500,为散客,如果大于500,为会员
1804   - if (kdAmount < 500)
1805   - {
1806   - memberInfo.Khlx = MemberTypeEnum.散客.GetHashCode().ToString();
1807   - }
1808   - else
1809   - {
1810   - memberInfo.Khlx = MemberTypeEnum.会员.GetHashCode().ToString();
  1541 + //清空品项明细
  1542 + await _db.Deleteable<LqKdPxmxEntity>().In(u => u.Glkdbh, ids).ExecuteCommandAsync();
  1543 +
  1544 + //清空扣款信息
  1545 + await _db.Deleteable<LqKdDeductinfoEntity>().In(u => u.BillingId, ids).ExecuteCommandAsync();
  1546 +
  1547 + //关闭事务
  1548 + _db.CommitTran();
1811 1549 }
1812   - await _db.Updateable(memberInfo).ExecuteCommandAsync();
1813   - // 批量插入扣款信息
1814   - if (allDeductEntities.Any())
  1550 + catch (Exception ex)
1815 1551 {
1816   - await _db.Insertable(allDeductEntities).ExecuteCommandAsync();
  1552 + //回滚事务
  1553 + _db.RollbackTran();
  1554 + throw NCCException.Oh($"批量删除开单记录表失败: {ex.Message}");
1817 1555 }
1818   - // 批量插入品项明细
1819   - if (allPxmxEntities.Any())
  1556 + }
  1557 + }
  1558 + #endregion
  1559 +
  1560 + #region 删除开单记录表
  1561 + /// <summary>
  1562 + /// 删除开单记录表
  1563 + /// </summary>
  1564 + /// <returns></returns>
  1565 + [HttpDelete("{id}")]
  1566 + public async Task Delete(string id)
  1567 + {
  1568 + var entity = await _db.Queryable<LqKdKdjlbEntity>().FirstAsync(p => p.Id == id);
  1569 + _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
  1570 + try
  1571 + {
  1572 + //开启事务
  1573 + _db.BeginTran();
  1574 + //先查询开单品项明细
  1575 + var lqPxmxList = _db.Queryable<LqKdPxmxEntity>().Where(x => x.Glkdbh == id).ToList();
  1576 + //判断是否有对应的消耗记录
  1577 + var consumeRecords = await _db.Queryable<LqXhPxmxEntity>().Where(x => lqPxmxList.Select(y => y.Id).Contains(x.BillingItemId)).AnyAsync();
  1578 + if (consumeRecords)
1820 1579 {
1821   - await _db.Insertable(allPxmxEntities).ExecuteCommandAsync();
  1580 + throw NCCException.Oh("选中的开单记录中有已消耗的记录,不能删除");
1822 1581 }
1823   - // 批量插入健康师业绩
1824   - if (allJksyjEntities.Any())
  1582 + //判断是否有对应的退卡记录
  1583 + var refundRecords = await _db.Queryable<LqHytkMxEntity>().Where(x => lqPxmxList.Select(y => y.Id).Contains(x.BillingItemId)).AnyAsync();
  1584 + if (refundRecords)
1825 1585 {
1826   - await _db.Insertable(allJksyjEntities).ExecuteCommandAsync();
  1586 + throw NCCException.Oh("选中的开单记录中有退卡记录,不能删除");
1827 1587 }
1828   - // 批量插入科技部老师业绩
1829   - if (allKjbsyjEntities.Any())
  1588 + //判断是否有对应的储扣记录
  1589 + var deductRecords = await _db.Queryable<LqKdDeductinfoEntity>().Where(x => lqPxmxList.Select(y => y.Id).Contains(x.DeductId)).AnyAsync();
  1590 + if (deductRecords)
1830 1591 {
1831   - await _db.Insertable(allKjbsyjEntities).ExecuteCommandAsync();
  1592 + throw NCCException.Oh("选中的开单记录中有储扣记录,不能删除");
1832 1593 }
1833 1594  
  1595 + //删除开单记录表记录
  1596 + await _db.Deleteable<LqKdKdjlbEntity>().Where(d => d.Id == id).ExecuteCommandAsync();
  1597 +
  1598 + //清空健康师业绩
  1599 + await _db.Deleteable<LqKdJksyjEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
  1600 +
  1601 + //清空科技部老师业绩
  1602 + await _db.Deleteable<LqKdKjbsyjEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
  1603 +
  1604 + //清空品项明细
  1605 + await _db.Deleteable<LqKdPxmxEntity>().Where(u => u.Glkdbh == id).ExecuteCommandAsync();
  1606 +
  1607 + //清空扣款信息
  1608 + await _db.Deleteable<LqKdDeductinfoEntity>().Where(u => u.BillingId == id).ExecuteCommandAsync();
  1609 +
1834 1610 //关闭事务
1835 1611 _db.CommitTran();
1836 1612 }
... ... @@ -1838,11 +1614,49 @@ namespace NCC.Extend.LqKdKdjlb
1838 1614 {
1839 1615 //回滚事务
1840 1616 _db.RollbackTran();
1841   - throw NCCException.Oh($"创建开单记录失败: {ex.Message}");
  1617 + throw NCCException.Oh($"删除开单记录表失败: {ex.Message}");
1842 1618 }
1843 1619 }
1844 1620 #endregion
1845 1621  
  1622 + #region 获取扣款类型枚举内容
  1623 + /// <summary>
  1624 + /// 获取扣款类型枚举内容
  1625 + /// </summary>
  1626 + /// <returns>扣款类型枚举列表</returns>
  1627 + [HttpGet("deduct-types")]
  1628 + public List<EnumOutput> GetDeductTypes()
  1629 + {
  1630 + return Enum.GetValues<DeductTypeEnum>()
  1631 + .Select(e => new EnumOutput
  1632 + {
  1633 + Value = (int)e,
  1634 + Name = e.ToString(),
  1635 + Description = e.GetDescription(),
  1636 + })
  1637 + .ToList();
  1638 + }
  1639 + #endregion
  1640 +
  1641 + #region 获取状态枚举内容(所有的状态通用)
  1642 + /// <summary>
  1643 + /// 获取状态枚举内容
  1644 + /// </summary>
  1645 + /// <returns>状态枚举列表</returns>
  1646 + [HttpGet("status-enum")]
  1647 + public List<EnumOutput> GetStatusEnum()
  1648 + {
  1649 + return Enum.GetValues<StatusEnum>()
  1650 + .Select(e => new EnumOutput
  1651 + {
  1652 + Value = (int)e,
  1653 + Name = e.ToString(),
  1654 + Description = e.GetDescription(),
  1655 + })
  1656 + .ToList();
  1657 + }
  1658 + #endregion
  1659 +
1846 1660 #region 作废开单记录
1847 1661 /// <summary>
1848 1662 /// 作废开单记录
... ... @@ -2325,7 +2139,7 @@ namespace NCC.Extend.LqKdKdjlb
2325 2139 }
2326 2140 #endregion
2327 2141  
2328   - #region 根据会员id获取会员的开单品项列表
  2142 + #region 根据会员id获取会员的开单品项列表
2329 2143 /// <summary>
2330 2144 /// 根据会员id获取会员的开单品项列表
2331 2145 /// </summary>
... ... @@ -2515,9 +2329,9 @@ namespace NCC.Extend.LqKdKdjlb
2515 2329 }
2516 2330 #endregion
2517 2331  
2518   - #region 获取门店某个时间段的开单记录汇总信息
  2332 + #region 获取门店某个时间段的开单记录汇总信息(台账)
2519 2333 /// <summary>
2520   - /// 获取门店某个时间段的开单记录汇总信息
  2334 + /// 获取门店某个时间段的开单记录汇总信息(台账)
2521 2335 /// </summary>
2522 2336 /// <param name="input">查询参数</param>
2523 2337 /// <returns>开单记录汇总信息</returns>
... ... @@ -2539,9 +2353,18 @@ namespace NCC.Extend.LqKdKdjlb
2539 2353 input.EndTime = DateTime.Now.AddDays(DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month));
2540 2354 }
2541 2355  
  2356 + // 构建开单记录查询条件
  2357 + var billingQuery = _db.Queryable<LqKdKdjlbEntity>()
  2358 + .Where(w => w.Djmd == input.StoreId && w.Kdrq >= input.StartTime && w.Kdrq <= input.EndTime && w.IsEffective == StatusEnum.有效.GetHashCode());
  2359 +
  2360 + // 客户筛选
  2361 + if (!string.IsNullOrEmpty(input.MemberId))
  2362 + {
  2363 + billingQuery = billingQuery.Where(w => w.Kdhy == input.MemberId);
  2364 + }
  2365 +
2542 2366 // 查询开单记录
2543   - var billingRecords = await _db.Queryable<LqKdKdjlbEntity>()
2544   - .Where(w => w.Djmd == input.StoreId && w.Kdrq >= input.StartTime && w.Kdrq <= input.EndTime && w.IsEffective == StatusEnum.有效.GetHashCode())
  2367 + var billingRecords = await billingQuery
2545 2368 .Select(it => new
2546 2369 {
2547 2370 id = it.Id,
... ... @@ -2552,12 +2375,15 @@ namespace NCC.Extend.LqKdKdjlb
2552 2375 zdyj = it.Zdyj,
2553 2376 sfyj = it.Sfyj,
2554 2377 qk = it.Qk,
  2378 + hgjg = it.Hgjg,
  2379 + hgjgName = SqlFunc.Subqueryable<LqHzfEntity>().Where(x => x.Id == it.Hgjg).Select(x => x.Hzmc),
2555 2380 kdhyc = SqlFunc.Subqueryable<LqKhxxEntity>().Where(x => x.Id == it.Kdhy).Select(x => x.Khmc),
2556 2381 kdhysjh = SqlFunc.Subqueryable<LqKhxxEntity>().Where(x => x.Id == it.Kdhy).Select(x => x.Sjh),
2557 2382 fkfs = it.Fkfs, // 付款方式
2558 2383 khly = it.Khly, // 客户来源
2559 2384 bz = it.Bz, // 备注
2560 2385 createTime = it.CreateTime
  2386 +
2561 2387 })
2562 2388 .ToListAsync();
2563 2389  
... ... @@ -2594,9 +2420,24 @@ namespace NCC.Extend.LqKdKdjlb
2594 2420  
2595 2421 var billingIds = billingRecords.Select(x => x.id).ToList();
2596 2422  
  2423 + // 构建品项明细查询条件
  2424 + var itemDetailsQuery = _db.Queryable<LqKdPxmxEntity>()
  2425 + .Where(w => billingIds.Contains(w.Glkdbh) && w.IsEffective == StatusEnum.有效.GetHashCode());
  2426 +
  2427 + // 品项分类筛选
  2428 + if (!string.IsNullOrEmpty(input.ItemCategory))
  2429 + {
  2430 + itemDetailsQuery = itemDetailsQuery.Where(w => w.ItemCategory == input.ItemCategory);
  2431 + }
  2432 +
  2433 + // 品项筛选
  2434 + if (!string.IsNullOrEmpty(input.ItemId))
  2435 + {
  2436 + itemDetailsQuery = itemDetailsQuery.Where(w => w.Px == input.ItemId);
  2437 + }
  2438 +
2597 2439 // 查询品项明细,按F_SourceType分类
2598   - var itemDetails = await _db.Queryable<LqKdPxmxEntity>()
2599   - .Where(w => billingIds.Contains(w.Glkdbh) && w.IsEffective == StatusEnum.有效.GetHashCode())
  2440 + var itemDetails = await itemDetailsQuery
2600 2441 .Select(it => new
2601 2442 {
2602 2443 id = it.Id,
... ... @@ -2618,9 +2459,30 @@ namespace NCC.Extend.LqKdKdjlb
2618 2459 var giftedItems = itemDetails.Where(x => x.sourceType == "赠送").ToList();
2619 2460 var experienceItems = itemDetails.Where(x => x.sourceType == "体验").ToList();
2620 2461  
  2462 + // 构建健康师业绩查询条件
  2463 + var healthTeacherQuery = _db.Queryable<LqKdJksyjEntity>()
  2464 + .Where(w => billingIds.Contains(w.Glkdbh) && w.IsEffective == StatusEnum.有效.GetHashCode());
  2465 +
  2466 + // 健康师筛选
  2467 + if (!string.IsNullOrEmpty(input.HealthCoachId))
  2468 + {
  2469 + healthTeacherQuery = healthTeacherQuery.Where(w => w.Jks == input.HealthCoachId || w.Jkszh == input.HealthCoachId);
  2470 + }
  2471 +
  2472 + // 品项分类筛选(健康师业绩表)
  2473 + if (!string.IsNullOrEmpty(input.ItemCategory))
  2474 + {
  2475 + healthTeacherQuery = healthTeacherQuery.Where(w => w.ItemCategory == input.ItemCategory);
  2476 + }
  2477 +
  2478 + // 品项筛选(健康师业绩表)
  2479 + if (!string.IsNullOrEmpty(input.ItemId))
  2480 + {
  2481 + healthTeacherQuery = healthTeacherQuery.Where(w => w.ItemId == input.ItemId);
  2482 + }
  2483 +
2621 2484 // 查询健康师业绩数据
2622   - var healthTeacherData = await _db.Queryable<LqKdJksyjEntity>()
2623   - .Where(w => billingIds.Contains(w.Glkdbh) && w.IsEffective == StatusEnum.有效.GetHashCode())
  2485 + var healthTeacherData = await healthTeacherQuery
2624 2486 .Select(it => new
2625 2487 {
2626 2488 id = it.Id,
... ... @@ -2639,6 +2501,56 @@ namespace NCC.Extend.LqKdKdjlb
2639 2501 })
2640 2502 .ToListAsync();
2641 2503  
  2504 + // 根据筛选条件过滤开单记录
  2505 + // 收集所有需要匹配的开单ID(取交集)
  2506 + var matchedBillingIds = new List<string>();
  2507 + var hasFilterCondition = false; // 标记是否传入了筛选条件
  2508 +
  2509 + // 如果传入了品项分类或品项筛选条件,收集匹配的开单ID
  2510 + if (!string.IsNullOrEmpty(input.ItemCategory) || !string.IsNullOrEmpty(input.ItemId))
  2511 + {
  2512 + hasFilterCondition = true;
  2513 + var itemMatchedIds = itemDetails.Select(x => x.glkdbh).Distinct().ToList();
  2514 + if (matchedBillingIds.Any())
  2515 + {
  2516 + matchedBillingIds = matchedBillingIds.Intersect(itemMatchedIds).ToList();
  2517 + }
  2518 + else
  2519 + {
  2520 + matchedBillingIds = itemMatchedIds;
  2521 + }
  2522 + }
  2523 +
  2524 + // 如果传入了健康师筛选条件,收集匹配的开单ID
  2525 + if (!string.IsNullOrEmpty(input.HealthCoachId))
  2526 + {
  2527 + hasFilterCondition = true;
  2528 + var healthTeacherMatchedIds = healthTeacherData.Select(x => x.glkdbh).Distinct().ToList();
  2529 + if (matchedBillingIds.Any())
  2530 + {
  2531 + matchedBillingIds = matchedBillingIds.Intersect(healthTeacherMatchedIds).ToList();
  2532 + }
  2533 + else
  2534 + {
  2535 + matchedBillingIds = healthTeacherMatchedIds;
  2536 + }
  2537 + }
  2538 +
  2539 + // 如果传入了筛选条件,必须过滤开单记录(即使没有匹配数据也要过滤,返回空结果)
  2540 + if (hasFilterCondition)
  2541 + {
  2542 + if (matchedBillingIds.Any())
  2543 + {
  2544 + billingRecords = billingRecords.Where(x => matchedBillingIds.Contains(x.id)).ToList();
  2545 + }
  2546 + else
  2547 + {
  2548 + // 传入了筛选条件但没有匹配数据,返回空结果
  2549 + billingRecords = billingRecords.Where(x => false).ToList();
  2550 + }
  2551 + billingIds = billingRecords.Select(x => x.id).ToList();
  2552 + }
  2553 +
2642 2554 // 构建按开单记录分组的详细数据
2643 2555 var detailedRecords = billingRecords.Select(billing => new
2644 2556 {
... ... @@ -2649,7 +2561,8 @@ namespace NCC.Extend.LqKdKdjlb
2649 2561 storeId = billing.djmd,
2650 2562 goldTriangle = billing.jsj,
2651 2563 customerType = billing.gjlx,
2652   -
  2564 + cooperationInstitution = billing.hgjg,
  2565 + cooperationInstitutionName = billing.hgjgName,
2653 2566 // 品项分类
2654 2567 purchasedItems = itemDetails.Where(x => x.glkdbh == billing.id && x.sourceType == "购买").Select(x => new
2655 2568 {
... ... @@ -2930,7 +2843,6 @@ namespace NCC.Extend.LqKdKdjlb
2930 2843 // 4. 创建退卡品项明细和业绩记录
2931 2844 var refundMxEntities = new List<LqHytkMxEntity>();
2932 2845 var refundJksyjEntities = new List<LqHytkJksyjEntity>();
2933   -
2934 2846 var refundKjbsyjEntities = new List<LqHytkKjbsyjEntity>();
2935 2847  
2936 2848 foreach (var item in input.TransferItems)
... ... @@ -2956,6 +2868,7 @@ namespace NCC.Extend.LqKdKdjlb
2956 2868 TotalPrice = totalItemDeduction,
2957 2869 IsEffective = StatusEnum.有效.GetHashCode(),
2958 2870 ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Qt2).FirstAsync(),
  2871 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Fl3).FirstAsync() ?? "",
2959 2872 };
2960 2873 refundMxEntities.Add(refundMxEntity);
2961 2874 var refundKdyjEntities = _db.Queryable<LqKdJksyjEntity>().Where(p => p.Kdpxid == item.BillingItemId).ToList();
... ... @@ -2978,7 +2891,7 @@ namespace NCC.Extend.LqKdKdjlb
2978 2891 Jks = jks.Jkszh,
2979 2892 Jksxm = jks.Jksxm,
2980 2893 Jkszh = jks.Jkszh,
2981   - Jksyj = (totalItemDeduction / refundKdyjEntities.Count()),
  2894 + Jksyj = totalItemDeduction / refundKdyjEntities.Count(),
2982 2895 Tksj = DateTime.Now,
2983 2896 F_jsjid = GetMonTeam.JsjId,
2984 2897 F_tkpxid = refundMxEntity.Id,
... ... @@ -2990,7 +2903,8 @@ namespace NCC.Extend.LqKdKdjlb
2990 2903 ItemCategory = refundMxEntity.ItemCategory,
2991 2904 ItemId = refundMxEntity.Px,
2992 2905 StoreId = refundEntity.Md,
2993   - ItemName = refundMxEntity.Pxmc
  2906 + ItemName = refundMxEntity.Pxmc,
  2907 + PerformanceType = refundMxEntity.PerformanceType
2994 2908 });
2995 2909 }
2996 2910 //查询科技部老师的业绩
... ... @@ -3017,7 +2931,8 @@ namespace NCC.Extend.LqKdKdjlb
3017 2931 ItemCategory = refundMxEntity.ItemCategory,
3018 2932 ItemId = refundMxEntity.Px,
3019 2933 StoreId = refundEntity.Md,
3020   - ItemName = refundMxEntity.Pxmc
  2934 + ItemName = refundMxEntity.Pxmc,
  2935 + PerformanceType = refundMxEntity.PerformanceType
3021 2936 });
3022 2937 }
3023 2938 }
... ... @@ -3060,6 +2975,29 @@ namespace NCC.Extend.LqKdKdjlb
3060 2975 foreach (var item in input.TransferItems)
3061 2976 {
3062 2977 var refundPxmxEntity = await _db.Queryable<LqKdPxmxEntity>().Where(p => p.Id == item.BillingItemId).FirstAsync();
  2978 + // 计算转卡金额和数量
  2979 + var transferAmount = item.ItemPrice * item.TransferQuantity;
  2980 + var transferQuantity = item.TransferQuantity;
  2981 +
  2982 + // 获取原开单品项的总金额和总数量
  2983 + var originalTotalPrice = refundPxmxEntity.TotalPrice > 0 ? refundPxmxEntity.TotalPrice : refundPxmxEntity.ActualPrice;
  2984 + var originalProjectNumber = refundPxmxEntity.ProjectNumber > 0 ? refundPxmxEntity.ProjectNumber : 1;
  2985 +
  2986 + // 计算转卡比例(优先使用金额比例,如果金额为0则使用数量比例)
  2987 + decimal transferRatio = 0;
  2988 + if (originalTotalPrice > 0)
  2989 + {
  2990 + transferRatio = transferAmount / originalTotalPrice;
  2991 + }
  2992 + else if (originalProjectNumber > 0)
  2993 + {
  2994 + transferRatio = (decimal)transferQuantity / originalProjectNumber;
  2995 + }
  2996 + else
  2997 + {
  2998 + transferRatio = 1; // 如果原数据异常,默认按100%计算
  2999 + }
  3000 +
3063 3001 var billingPxmxEntity = new LqKdPxmxEntity
3064 3002 {
3065 3003 Id = YitIdHelper.NextId().ToString(),
... ... @@ -3072,53 +3010,142 @@ namespace NCC.Extend.LqKdKdjlb
3072 3010 ProjectNumber = item.TransferQuantity,
3073 3011 IsEnabled = StatusEnum.有效.GetHashCode(),
3074 3012 SourceType = refundPxmxEntity.SourceType,
3075   - TotalPrice = item.ItemPrice * item.TransferQuantity,
3076   - ActualPrice = item.ItemPrice * item.TransferQuantity,
  3013 + TotalPrice = transferAmount,
  3014 + ActualPrice = transferAmount,
3077 3015 IsEffective = StatusEnum.有效.GetHashCode(),
3078 3016 Remark = $"从会员 {fromMember.Khmc} 转入",
3079 3017 ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Qt2).FirstAsync(),
  3018 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == refundPxmxEntity.Px).Select(x => x.Fl3).FirstAsync() ?? "",
3080 3019 };
3081 3020 billingPxmxEntities.Add(billingPxmxEntity);
3082 3021  
3083   - foreach (var jks in refundJksyjEntities)
  3022 + // 查询原开单记录的健康师业绩
  3023 + var billingKdyjEntitiesList = _db.Queryable<LqKdJksyjEntity>().Where(p => p.Kdpxid == item.BillingItemId).ToList();
  3024 + if (billingKdyjEntitiesList.Any())
3084 3025 {
3085   - billingJksyjEntities.Add(new LqKdJksyjEntity
  3026 + // 计算原开单所有健康师的总业绩
  3027 + var originalTotalJksyj = billingKdyjEntitiesList.Sum(x => decimal.Parse(x.Jksyj ?? "0"));
  3028 + // 如果原开单总业绩为0,则按转卡金额均分
  3029 + if (originalTotalJksyj == 0)
3086 3030 {
3087   - Id = YitIdHelper.NextId().ToString(),
3088   - Glkdbh = billingId,
3089   - Jks = jks.Jkszh,
3090   - Jksxm = jks.Jksxm,
3091   - Jkszh = jks.Jkszh,
3092   - Jksyj = jks.Jksyj.ToString(),
3093   - Yjsj = transferTime,
3094   - Jsj_id = jks.F_jsjid,
3095   - Kdpxid = billingPxmxEntity.Id,
3096   - IsEffective = StatusEnum.有效.GetHashCode(),
3097   - ItemCategory = billingPxmxEntity.ItemCategory,
3098   - ItemId = billingPxmxEntity.Px,
3099   - StoreId = billingEntity.Djmd,
3100   - ItemName = billingPxmxEntity.Pxmc
3101   - });
  3031 + var avgJksyj = transferAmount / billingKdyjEntitiesList.Count;
  3032 + foreach (var jks in billingKdyjEntitiesList)
  3033 + {
  3034 + billingJksyjEntities.Add(new LqKdJksyjEntity
  3035 + {
  3036 + Id = YitIdHelper.NextId().ToString(),
  3037 + Glkdbh = billingId,
  3038 + Jks = jks.Jkszh,
  3039 + Jksxm = jks.Jksxm,
  3040 + Jkszh = jks.Jkszh,
  3041 + Jksyj = avgJksyj.ToString("F2"),
  3042 + Yjsj = transferTime,
  3043 + Jsj_id = jks.Jsj_id,
  3044 + Kdpxid = billingPxmxEntity.Id,
  3045 + IsEffective = StatusEnum.有效.GetHashCode(),
  3046 + ItemCategory = billingPxmxEntity.ItemCategory,
  3047 + ItemId = billingPxmxEntity.Px,
  3048 + StoreId = billingEntity.Djmd,
  3049 + ItemName = billingPxmxEntity.Pxmc,
  3050 + PerformanceType = billingPxmxEntity.PerformanceType
  3051 + });
  3052 + }
  3053 + }
  3054 + else
  3055 + {
  3056 + // 按原业绩比例计算转卡后的业绩
  3057 + foreach (var jks in billingKdyjEntitiesList)
  3058 + {
  3059 + var originalJksyj = decimal.Parse(jks.Jksyj ?? "0");
  3060 + // 计算该健康师在原开单中的业绩占比
  3061 + var jksRatio = originalTotalJksyj > 0 ? originalJksyj / originalTotalJksyj : 0;
  3062 + // 转卡后的业绩 = 转卡金额 × 该健康师的业绩占比
  3063 + var transferJksyj = transferAmount * jksRatio;
  3064 +
  3065 + billingJksyjEntities.Add(new LqKdJksyjEntity
  3066 + {
  3067 + Id = YitIdHelper.NextId().ToString(),
  3068 + Glkdbh = billingId,
  3069 + Jks = jks.Jkszh,
  3070 + Jksxm = jks.Jksxm,
  3071 + Jkszh = jks.Jkszh,
  3072 + Jksyj = transferJksyj.ToString("F2"),
  3073 + Yjsj = transferTime,
  3074 + Jsj_id = jks.Jsj_id,
  3075 + Kdpxid = billingPxmxEntity.Id,
  3076 + IsEffective = StatusEnum.有效.GetHashCode(),
  3077 + ItemCategory = billingPxmxEntity.ItemCategory,
  3078 + ItemId = billingPxmxEntity.Px,
  3079 + StoreId = billingEntity.Djmd,
  3080 + ItemName = billingPxmxEntity.Pxmc,
  3081 + PerformanceType = billingPxmxEntity.PerformanceType
  3082 + });
  3083 + }
  3084 + }
3102 3085 }
3103 3086  
3104   - foreach (var kjbs in refundKjbsyjEntities)
  3087 + // 查询原开单记录的科技部老师业绩
  3088 + var billingKjbsyjEntitiesList = _db.Queryable<LqKdKjbsyjEntity>().Where(p => p.Kdpxid == item.BillingItemId).ToList();
  3089 + if (billingKjbsyjEntitiesList.Any())
3105 3090 {
3106   - billingKjbsyjEntities.Add(new LqKdKjbsyjEntity
  3091 + // 计算原开单所有科技部老师的总业绩
  3092 + var originalTotalKjbsyj = billingKjbsyjEntitiesList.Sum(x => decimal.Parse(x.Kjblsyj ?? "0"));
  3093 +
  3094 + // 如果原开单总业绩为0,则按转卡金额均分
  3095 + if (originalTotalKjbsyj == 0)
3107 3096 {
3108   - Id = YitIdHelper.NextId().ToString(),
3109   - Glkdbh = billingId,
3110   - Kjbls = kjbs.Kjbls,
3111   - Kjblsxm = kjbs.Kjblsxm,
3112   - Kjblszh = kjbs.Kjblszh,
3113   - Kjblsyj = kjbs.Kjblsyj.ToString(),
3114   - Yjsj = transferTime,
3115   - Kdpxid = billingPxmxEntity.Id,
3116   - IsEffective = StatusEnum.有效.GetHashCode(),
3117   - ItemCategory = billingPxmxEntity.ItemCategory,
3118   - ItemId = billingPxmxEntity.Px,
3119   - StoreId = billingEntity.Djmd,
3120   - ItemName = billingPxmxEntity.Pxmc
3121   - });
  3097 + var avgKjbsyj = transferAmount / billingKjbsyjEntitiesList.Count;
  3098 + foreach (var kjbs in billingKjbsyjEntitiesList)
  3099 + {
  3100 + billingKjbsyjEntities.Add(new LqKdKjbsyjEntity
  3101 + {
  3102 + Id = YitIdHelper.NextId().ToString(),
  3103 + Glkdbh = billingId,
  3104 + Kjbls = kjbs.Kjbls,
  3105 + Kjblsxm = kjbs.Kjblsxm,
  3106 + Kjblszh = kjbs.Kjblszh,
  3107 + Kjblsyj = avgKjbsyj.ToString("F2"),
  3108 + Yjsj = transferTime,
  3109 + Kdpxid = billingPxmxEntity.Id,
  3110 + IsEffective = StatusEnum.有效.GetHashCode(),
  3111 + ItemCategory = billingPxmxEntity.ItemCategory,
  3112 + ItemId = billingPxmxEntity.Px,
  3113 + StoreId = billingEntity.Djmd,
  3114 + ItemName = billingPxmxEntity.Pxmc,
  3115 + PerformanceType = billingPxmxEntity.PerformanceType
  3116 + });
  3117 + }
  3118 + }
  3119 + else
  3120 + {
  3121 + // 按原业绩比例计算转卡后的业绩
  3122 + foreach (var kjbs in billingKjbsyjEntitiesList)
  3123 + {
  3124 + var originalKjbsyj = decimal.Parse(kjbs.Kjblsyj ?? "0");
  3125 + // 计算该科技部老师在原开单中的业绩占比
  3126 + var kjbsRatio = originalTotalKjbsyj > 0 ? originalKjbsyj / originalTotalKjbsyj : 0;
  3127 + // 转卡后的业绩 = 转卡金额 × 该科技部老师的业绩占比
  3128 + var transferKjbsyj = transferAmount * kjbsRatio;
  3129 +
  3130 + billingKjbsyjEntities.Add(new LqKdKjbsyjEntity
  3131 + {
  3132 + Id = YitIdHelper.NextId().ToString(),
  3133 + Glkdbh = billingId,
  3134 + Kjbls = kjbs.Kjbls,
  3135 + Kjblsxm = kjbs.Kjblsxm,
  3136 + Kjblszh = kjbs.Kjblszh,
  3137 + Kjblsyj = transferKjbsyj.ToString("F2"),
  3138 + Yjsj = transferTime,
  3139 + Kdpxid = billingPxmxEntity.Id,
  3140 + IsEffective = StatusEnum.有效.GetHashCode(),
  3141 + ItemCategory = billingPxmxEntity.ItemCategory,
  3142 + ItemId = billingPxmxEntity.Px,
  3143 + StoreId = billingEntity.Djmd,
  3144 + ItemName = billingPxmxEntity.Pxmc,
  3145 + PerformanceType = billingPxmxEntity.PerformanceType
  3146 + });
  3147 + }
  3148 + }
3122 3149 }
3123 3150 }
3124 3151 //记录转卡日志
... ... @@ -3258,6 +3285,9 @@ namespace NCC.Extend.LqKdKdjlb
3258 3285 /// - PersonCount: 人次(按客户+日期去重,同一客户不同天算多次)
3259 3286 /// - ProjectCount: 消耗项目数(项目总次数)
3260 3287 /// - RefundAmount: 退卡金额(健康师退卡业绩总金额)
  3288 + /// - LaborCost: 手工费(消耗时的手工费总金额)
  3289 + /// - OriginalLaborCost: 原始手工费(消耗时的原始手工费总金额)
  3290 + /// - OvertimeLaborCost: 加班手工费(消耗时的加班手工费总金额)
3261 3291 /// </remarks>
3262 3292 /// <param name="input">查询参数</param>
3263 3293 /// <returns>健康师统计数据列表</returns>
... ... @@ -3305,7 +3335,12 @@ namespace NCC.Extend.LqKdKdjlb
3305 3335 CAST(COALESCE(consume_stats.ProjectCount, 0) AS DECIMAL(18,2)) as ProjectCount,
3306 3336  
3307 3337 -- 退卡金额
3308   - COALESCE(refund_stats.RefundAmount, 0) as RefundAmount
  3338 + COALESCE(refund_stats.RefundAmount, 0) as RefundAmount,
  3339 +
  3340 + -- 手工费相关统计
  3341 + COALESCE(consume_stats.LaborCost, 0) as LaborCost,
  3342 + COALESCE(consume_stats.OriginalLaborCost, 0) as OriginalLaborCost,
  3343 + COALESCE(consume_stats.OvertimeLaborCost, 0) as OvertimeLaborCost
3309 3344  
3310 3345 FROM BASE_USER u
3311 3346 LEFT JOIN lq_mdxx md ON u.F_MDID = md.F_Id
... ... @@ -3365,17 +3400,20 @@ namespace NCC.Extend.LqKdKdjlb
3365 3400 -- 消耗统计子查询
3366 3401 LEFT JOIN (
3367 3402 SELECT
3368   - jksyj.jkszh as EmployeeId,
  3403 + jksyj.jks as EmployeeId,
3369 3404 SUM(jksyj.jksyj) as ConsumeAmount,
3370   - CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount
  3405 + CAST(SUM(jksyj.F_kdpxNumber) AS DECIMAL(18,2)) as ProjectCount,
  3406 + COALESCE(SUM(jksyj.F_LaborCost), 0) as LaborCost,
  3407 + COALESCE(SUM(jksyj.F_OriginalLaborCost), 0) as OriginalLaborCost,
  3408 + COALESCE(SUM(jksyj.F_OvertimeLaborCost), 0) as OvertimeLaborCost
3371 3409 FROM lq_xh_jksyj jksyj
3372 3410 INNER JOIN lq_xh_hyhk hyhk ON jksyj.glkdbh = hyhk.F_Id
3373   - WHERE jksyj.jkszh IS NOT NULL
  3411 + WHERE jksyj.jks IS NOT NULL
3374 3412 AND jksyj.F_IsEffective = 1
3375 3413 AND hyhk.F_IsEffective = 1
3376 3414 AND hyhk.hksj >= @startTime
3377 3415 AND hyhk.hksj <= @endTime
3378   - GROUP BY jksyj.jkszh
  3416 + GROUP BY jksyj.jks
3379 3417 ) consume_stats ON u.F_Id = consume_stats.EmployeeId
3380 3418  
3381 3419 -- 有效人头统计子查询(从人次记录表获取,按月份+客户+数量去重后累加数量,F_HasBilling=1)
... ... @@ -3831,7 +3869,11 @@ namespace NCC.Extend.LqKdKdjlb
3831 3869 var sidx = string.IsNullOrEmpty(input.sidx) ? "CreateTime" : input.sidx;
3832 3870  
3833 3871 // 构建基础查询:储扣信息 JOIN 开单记录 JOIN 客户信息 JOIN 门店信息
3834   - var baseQuery = _db.Queryable<LqKdDeductinfoEntity, LqKdKdjlbEntity, LqKhxxEntity, LqMdxxEntity>((deduct, billing, member, store) => deduct.BillingId == billing.Id && billing.Kdhy == member.Id && billing.Djmd == store.Id)
  3872 + var baseQuery = _db.Queryable<LqKdDeductinfoEntity, LqKdKdjlbEntity, LqKhxxEntity, LqMdxxEntity>(
  3873 + (deduct, billing, member, store) =>
  3874 + deduct.BillingId == billing.Id &&
  3875 + billing.Kdhy == member.Id &&
  3876 + billing.Djmd == store.Id)
3835 3877 .WhereIF(!string.IsNullOrEmpty(input.DeductType), (deduct, billing, member, store) => deduct.DeductType == input.DeductType)
3836 3878 .WhereIF(!string.IsNullOrEmpty(input.DeductId), (deduct, billing, member, store) => deduct.DeductId == input.DeductId)
3837 3879 .WhereIF(!string.IsNullOrEmpty(input.BillingId), (deduct, billing, member, store) => deduct.BillingId == input.BillingId)
... ... @@ -3851,7 +3893,7 @@ namespace NCC.Extend.LqKdKdjlb
3851 3893 (billing.Id != null && billing.Id.Contains(input.keyword)));
3852 3894  
3853 3895  
3854   - // 查询并分页
  3896 + // 查询并分页,使用子查询获取开单类型
3855 3897 var data = await baseQuery.Select((deduct, billing, member, store) => new LqKdDeductinfoListOutput
3856 3898 {
3857 3899 Id = deduct.Id ?? "",
... ... @@ -3871,7 +3913,12 @@ namespace NCC.Extend.LqKdKdjlb
3871 3913 MemberName = member.Khmc ?? "",
3872 3914 MemberPhone = member.Sjh ?? "",
3873 3915 StoreId = billing.Djmd ?? "",
3874   - StoreName = store.Dm ?? ""
  3916 + StoreName = store.Dm ?? "",
  3917 + TimePeriod = billing.Kdrq,
  3918 + BillingType = SqlFunc.Subqueryable<LqKdPxmxEntity>()
  3919 + .Where(pxmx => pxmx.Id == deduct.DeductId && pxmx.Px == deduct.ItemId)
  3920 + .Select(pxmx => pxmx.SourceType),
  3921 + CooperationInstitution = billing.Hgjg ?? ""
3875 3922 }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
3876 3923  
3877 3924 return PageResult<LqKdDeductinfoListOutput>.SqlSugarPageResult(data);
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqProductService.cs
... ... @@ -254,6 +254,7 @@ namespace NCC.Extend
254 254 var productIds = data.list.Select(x => x.id).ToList();
255 255 if (productIds.Any())
256 256 {
  257 + // 查询总库存数量
257 258 var inventoryDict = await _db.Queryable<LqInventoryEntity>()
258 259 .Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode())
259 260 .GroupBy(x => x.ProductId)
... ... @@ -262,10 +263,21 @@ namespace NCC.Extend
262 263  
263 264 var inventoryDictMap = inventoryDict.ToDictionary(k => k.ProductId, v => v.TotalQuantity.HasValue ? (int)v.TotalQuantity.Value : 0);
264 265  
265   - // 填充库存数量
  266 + // 查询已使用数量
  267 + var usageDict = await _db.Queryable<LqInventoryUsageEntity>()
  268 + .Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode())
  269 + .GroupBy(x => x.ProductId)
  270 + .Select(x => new { ProductId = x.ProductId, TotalUsage = SqlFunc.AggregateSum((decimal?)x.UsageQuantity) })
  271 + .ToListAsync();
  272 +
  273 + var usageDictMap = usageDict.ToDictionary(k => k.ProductId, v => v.TotalUsage.HasValue ? (int)v.TotalUsage.Value : 0);
  274 +
  275 + // 填充库存数量(总库存减去已使用数量)
266 276 foreach (var item in data.list)
267 277 {
268   - item.currentInventory = inventoryDictMap.ContainsKey(item.id) ? inventoryDictMap[item.id] : 0;
  278 + var totalInventory = inventoryDictMap.ContainsKey(item.id) ? inventoryDictMap[item.id] : 0;
  279 + var totalUsage = usageDictMap.ContainsKey(item.id) ? usageDictMap[item.id] : 0;
  280 + item.currentInventory = totalInventory - totalUsage;
269 281 }
270 282 }
271 283  
... ... @@ -427,6 +439,7 @@ namespace NCC.Extend
427 439 var productIds = products.Select(x => x.id).ToList();
428 440 if (productIds.Any())
429 441 {
  442 + // 查询总库存数量
430 443 var inventoryDict = await _db.Queryable<LqInventoryEntity>()
431 444 .Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode())
432 445 .GroupBy(x => x.ProductId)
... ... @@ -435,10 +448,21 @@ namespace NCC.Extend
435 448  
436 449 var inventoryDictMap = inventoryDict.ToDictionary(k => k.ProductId, v => v.TotalQuantity.HasValue ? (int)v.TotalQuantity.Value : 0);
437 450  
438   - // 填充库存数量
  451 + // 查询已使用数量
  452 + var usageDict = await _db.Queryable<LqInventoryUsageEntity>()
  453 + .Where(x => productIds.Contains(x.ProductId) && x.IsEffective == StatusEnum.有效.GetHashCode())
  454 + .GroupBy(x => x.ProductId)
  455 + .Select(x => new { ProductId = x.ProductId, TotalUsage = SqlFunc.AggregateSum((decimal?)x.UsageQuantity) })
  456 + .ToListAsync();
  457 +
  458 + var usageDictMap = usageDict.ToDictionary(k => k.ProductId, v => v.TotalUsage.HasValue ? (int)v.TotalUsage.Value : 0);
  459 +
  460 + // 填充库存数量(总库存减去已使用数量)
439 461 foreach (var item in products)
440 462 {
441   - item.currentInventory = inventoryDictMap.ContainsKey(item.id) ? inventoryDictMap[item.id] : 0;
  463 + var totalInventory = inventoryDictMap.ContainsKey(item.id) ? inventoryDictMap[item.id] : 0;
  464 + var totalUsage = usageDictMap.ContainsKey(item.id) ? usageDictMap[item.id] : 0;
  465 + item.currentInventory = totalInventory - totalUsage;
442 466 }
443 467 }
444 468  
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqSalaryService.cs 0 → 100644
  1 +using Microsoft.AspNetCore.Authorization;
  2 +using Microsoft.AspNetCore.Mvc;
  3 +using NCC.Common.Enum;
  4 +using NCC.Common.Filter;
  5 +using NCC.Common.Helper;
  6 +using NCC.Dependency;
  7 +using NCC.DynamicApiController;
  8 +using NCC.Extend.Entitys.Dto.LqSalary;
  9 +using Yitter.IdGenerator;
  10 +using NCC.Extend.Entitys.lq_attendance_summary;
  11 +using NCC.Extend.Entitys.lq_jinsanjiao_user;
  12 +using NCC.Extend.Entitys.lq_kd_jksyj;
  13 +using NCC.Extend.Entitys.lq_kd_kdjlb;
  14 +using NCC.Extend.Entitys.lq_md_target;
  15 +using NCC.Extend.Entitys.lq_person_times_record;
  16 +using NCC.Extend.Entitys.lq_salary_statistics;
  17 +using NCC.Extend.Entitys.lq_xh_jksyj;
  18 +using NCC.Extend.Entitys.lq_ycsd_jsj;
  19 +using SqlSugar;
  20 +using System;
  21 +using System.Collections.Generic;
  22 +using System.Linq;
  23 +using System.Threading.Tasks;
  24 +
  25 +namespace NCC.Extend
  26 +{
  27 + /// <summary>
  28 + /// 薪酬服务
  29 + /// </summary>
  30 + [ApiDescriptionSettings(Tag = "薪酬服务", Name = "LqSalary", Order = 300)]
  31 + [Route("api/Extend/[controller]")]
  32 + public class LqSalaryService : IDynamicApiController, ITransient
  33 + {
  34 + private readonly ISqlSugarClient _db;
  35 +
  36 + /// <summary>
  37 + /// 初始化一个<see cref="LqSalaryService"/>类型的新实例
  38 + /// </summary>
  39 + public LqSalaryService(ISqlSugarClient db)
  40 + {
  41 + _db = db;
  42 + }
  43 +
  44 + /// <summary>
  45 + /// 获取健康师工资列表
  46 + /// </summary>
  47 + /// <param name="input">查询参数</param>
  48 + /// <returns>健康师工资分页列表</returns>
  49 + [HttpGet("health-coach")]
  50 + public async Task<PageResult<HealthCoachSalaryOutput>> GetHealthCoachSalaryList([FromQuery] HealthCoachSalaryInput input)
  51 + {
  52 + var monthStr = $"{input.Year}{input.Month:D2}";
  53 +
  54 + // 1. 检查当月是否已生成工资数据
  55 + var exists = await _db.Queryable<LqSalaryStatisticsEntity>()
  56 + .AnyAsync(x => x.StatisticsMonth == monthStr);
  57 +
  58 + // 2. 如果没有数据,则进行计算
  59 + if (!exists)
  60 + {
  61 + await CalculateHealthCoachSalary(input.Year, input.Month);
  62 + }
  63 +
  64 + // 3. 查询数据
  65 + var query = _db.Queryable<LqSalaryStatisticsEntity>()
  66 + .Where(x => x.StatisticsMonth == monthStr);
  67 +
  68 + if (!string.IsNullOrEmpty(input.StoreId))
  69 + {
  70 + query = query.Where(x => x.StoreId == input.StoreId);
  71 + }
  72 +
  73 + if (!string.IsNullOrEmpty(input.Keyword))
  74 + {
  75 + query = query.Where(x => x.EmployeeName.Contains(input.Keyword) || x.EmployeeId.Contains(input.Keyword));
  76 + }
  77 +
  78 + var list = await query.Select(x => new HealthCoachSalaryOutput
  79 + {
  80 + Id = x.Id,
  81 + StoreName = x.StoreName,
  82 + EmployeeName = x.EmployeeName,
  83 + Position = x.Position,
  84 + GoldTriangleTeam = x.GoldTriangleTeam,
  85 + TotalPerformance = x.TotalPerformance,
  86 + BasePerformance = x.BasePerformance,
  87 + CooperationPerformance = x.CooperationPerformance,
  88 + RewardPerformance = x.RewardPerformance,
  89 + Consumption = x.Consumption,
  90 + ProjectCount = x.ProjectCount,
  91 + CustomerCount = x.CustomerCount,
  92 + WorkingDays = x.WorkingDays,
  93 + HealthCoachBaseSalary = x.HealthCoachBaseSalary,
  94 + TotalCommission = x.TotalCommission,
  95 + HandworkFee = x.HandworkFee,
  96 + TotalSubsidy = x.TotalSubsidy,
  97 + TotalDeduction = x.TotalDeduction,
  98 + ActualSalary = x.ActualSalary,
  99 + IsLocked = x.IsLocked,
  100 + UpdateTime = x.UpdateTime
  101 + })
  102 + .ToPagedListAsync(input.currentPage, input.pageSize);
  103 +
  104 + return PageResult<HealthCoachSalaryOutput>.SqlSugarPageResult(list);
  105 + }
  106 +
  107 + /// <summary>
  108 + /// 计算健康师工资
  109 + /// </summary>
  110 + /// <param name="year">年份</param>
  111 + /// <param name="month">月份</param>
  112 + /// <returns></returns>
  113 + [HttpPost("calculate/health-coach")]
  114 + public async Task CalculateHealthCoachSalary(int year, int month)
  115 + {
  116 + var startDate = new DateTime(year, month, 1);
  117 + var endDate = startDate.AddMonths(1).AddDays(-1);
  118 + var monthStr = $"{year}{month:D2}";
  119 +
  120 + // 1. 获取基础数据
  121 +
  122 + // 1.1 业绩数据 (lq_kd_jksyj)
  123 + var performanceList = await _db.Queryable<LqKdJksyjEntity>()
  124 + .Where(x => x.Yjsj >= startDate && x.Yjsj <= endDate.AddDays(1) && x.IsEffective == 1)
  125 + .ToListAsync();
  126 +
  127 + // 1.1.1 获取关联的开单记录(用于获取 sfskdd)
  128 + var billingIds = performanceList.Select(x => x.Glkdbh).Distinct().ToList();
  129 + var billingDict = await _db.Queryable<LqKdKdjlbEntity>()
  130 + .Where(x => billingIds.Contains(x.Id))
  131 + .ToDictionaryAsync(x => x.Id, x => x.Sfskdd);
  132 +
  133 + // 1.1.2 组合数据
  134 + var performanceData = performanceList.Select(p => new
  135 + {
  136 + p.Jks,
  137 + p.Jksxm,
  138 + p.StoreId,
  139 + p.Jksyj,
  140 + p.ItemCategory,
  141 + Sfskdd = billingDict.ContainsKey(p.Glkdbh) ? billingDict[p.Glkdbh] : null
  142 + }).ToList();
  143 +
  144 + // 1.2 消耗数据 (lq_xh_jksyj)
  145 + var consumptionList = await _db.Queryable<LqXhJksyjEntity>()
  146 + .Where(x => x.Yjsj >= startDate && x.Yjsj <= endDate.AddDays(1) && x.IsEffective == 1)
  147 + .ToListAsync();
  148 +
  149 + // 1.3 考勤数据 (lq_attendance_summary)
  150 + var attendanceList = await _db.Queryable<LqAttendanceSummaryEntity>()
  151 + .Where(x => x.Year == year && x.Month == month && x.IsEffective == 1)
  152 + .ToListAsync();
  153 +
  154 + // 1.4 战队成员及顾问信息 (lq_jinsanjiao_user + lq_ycsd_jsj)
  155 + var teamUserList = await _db.Queryable<LqJinsanjiaoUserEntity>()
  156 + .Where(x => x.Month == monthStr && x.DeleteMark == 0)
  157 + .ToListAsync();
  158 +
  159 + // 1.4.1 获取战队信息
  160 + var teamIds = teamUserList.Select(x => x.JsjId).Distinct().ToList();
  161 + var teamList = await _db.Queryable<LqYcsdJsjEntity>()
  162 + .Where(x => teamIds.Contains(x.Id))
  163 + .ToListAsync();
  164 + var teamDict = teamList.ToDictionary(x => x.Id, x => x.Jsj);
  165 +
  166 + // 1.4.2 组合数据
  167 + var teamMembers = teamUserList.Select(user => new
  168 + {
  169 + user.UserId,
  170 + user.IsLeader,
  171 + TeamId = user.JsjId,
  172 + TeamName = teamDict.ContainsKey(user.JsjId) ? teamDict[user.JsjId] : (string)null
  173 + }).ToList();
  174 +
  175 + // 1.5 到店人头 (lq_person_times_record)
  176 + // 统计每个健康师的去重会员数
  177 + var headcountList = await _db.Queryable<LqPersonTimesRecordEntity>()
  178 + .Where(x => x.WorkMonth == monthStr && x.IsEffective == 1)
  179 + .GroupBy(x => x.PersonId)
  180 + .Select(x => new { PersonId = x.PersonId, Count = SqlFunc.AggregateDistinctCount(x.MemberId) })
  181 + .ToListAsync();
  182 +
  183 + // 1.6 门店生命线 (lq_md_target)
  184 + var storeTargets = await _db.Queryable<LqMdTargetEntity>()
  185 + .Where(x => x.Month == monthStr)
  186 + .ToListAsync();
  187 +
  188 + // 2. 聚合每个健康师的数据对象
  189 + var employeeStats = new Dictionary<string, LqSalaryStatisticsEntity>();
  190 +
  191 + // 获取所有涉及的健康师ID
  192 + var allEmployeeIds = performanceData.Select(x => x.Jks)
  193 + .Union(consumptionList.Select(x => x.Jks))
  194 + .Union(attendanceList.Select(x => x.UserId))
  195 + .Union(teamMembers.Select(x => x.UserId))
  196 + .Where(x => !string.IsNullOrEmpty(x))
  197 + .Distinct()
  198 + .ToList();
  199 +
  200 + foreach (var empId in allEmployeeIds)
  201 + {
  202 + var salary = new LqSalaryStatisticsEntity
  203 + {
  204 + Id = YitIdHelper.NextId().ToString(),
  205 + EmployeeId = empId,
  206 + StatisticsMonth = monthStr,
  207 + CreateTime = DateTime.Now,
  208 + UpdateTime = DateTime.Now,
  209 + IsLocked = 0
  210 + };
  211 +
  212 + // 填充基础信息 (姓名、门店)
  213 + var perfRecord = performanceData.FirstOrDefault(x => x.Jks == empId);
  214 + var consRecord = consumptionList.FirstOrDefault(x => x.Jks == empId);
  215 +
  216 + if (perfRecord != null)
  217 + {
  218 + salary.EmployeeName = perfRecord.Jksxm;
  219 + salary.StoreId = perfRecord.StoreId;
  220 + }
  221 + else if (consRecord != null)
  222 + {
  223 + salary.EmployeeName = consRecord.Jksxm;
  224 + salary.StoreId = consRecord.StoreId;
  225 + }
  226 +
  227 + // 填充门店名称
  228 + if (!string.IsNullOrEmpty(salary.StoreId))
  229 + {
  230 + // 这里简单处理,实际可能需要缓存门店列表
  231 + // salary.StoreName = ...
  232 + }
  233 +
  234 + // 2.1 计算个人业绩
  235 + var myPerf = performanceData.Where(x => x.Jks == empId).ToList();
  236 + salary.BasePerformance = myPerf.Where(x => x.ItemCategory == "基础业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
  237 + salary.CooperationPerformance = myPerf.Where(x => x.ItemCategory == "合作业绩").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
  238 + salary.TotalPerformance = myPerf.Sum(x => decimal.Parse(x.Jksyj ?? "0"));
  239 +
  240 + // 新客与升单业绩
  241 + salary.NewCustomerPerformance = myPerf.Where(x => x.Sfskdd == "是").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
  242 + salary.UpgradePerformance = myPerf.Where(x => x.Sfskdd == "否").Sum(x => decimal.Parse(x.Jksyj ?? "0"));
  243 +
  244 + // 2.2 计算消耗和项目数
  245 + var myCons = consumptionList.Where(x => x.Jks == empId).ToList();
  246 + salary.Consumption = myCons.Sum(x => x.Jksyj ?? 0);
  247 + salary.ProjectCount = myCons.Sum(x => x.KdpxNumber ?? 0);
  248 + salary.HandworkFee = myCons.Sum(x => x.LaborCost ?? 0); // 使用 F_LaborCost
  249 +
  250 + // 2.3 考勤数据
  251 + var myAtt = attendanceList.FirstOrDefault(x => x.UserId == empId);
  252 + salary.WorkingDays = myAtt?.WorkDays ?? 0;
  253 + salary.LeaveDays = myAtt?.LeaveDays ?? 0;
  254 +
  255 + // 2.4 到店人头
  256 + var myHeadcount = headcountList.FirstOrDefault(x => x.PersonId == empId);
  257 + salary.CustomerCount = myHeadcount?.Count ?? 0;
  258 +
  259 + // 2.5 战队信息 (初始)
  260 + var myTeam = teamMembers.FirstOrDefault(x => x.UserId == empId);
  261 + if (myTeam != null)
  262 + {
  263 + salary.GoldTriangleId = myTeam.TeamId;
  264 + salary.GoldTriangleTeam = myTeam.TeamName ?? "";
  265 + }
  266 +
  267 + employeeStats[empId] = salary;
  268 + }
  269 +
  270 + // 3. 处理战队逻辑 (考勤规则)
  271 + // 规则:若出勤天数 < 21天,则该健康师不计入战队,按单人计算。
  272 +
  273 + // 按战队分组
  274 + var teamGroups = employeeStats.Values
  275 + .Where(x => !string.IsNullOrEmpty(x.GoldTriangleId))
  276 + .GroupBy(x => x.GoldTriangleId)
  277 + .ToList();
  278 +
  279 + foreach (var group in teamGroups)
  280 + {
  281 + var validMembers = new List<LqSalaryStatisticsEntity>();
  282 + var invalidMembers = new List<LqSalaryStatisticsEntity>();
  283 +
  284 + foreach (var member in group)
  285 + {
  286 + if (member.WorkingDays >= 21)
  287 + {
  288 + validMembers.Add(member);
  289 + }
  290 + else
  291 + {
  292 + invalidMembers.Add(member);
  293 + }
  294 + }
  295 +
  296 + // 对于无效成员,移除战队标识,视为单人
  297 + foreach (var member in invalidMembers)
  298 + {
  299 + member.GoldTriangleId = null;
  300 + member.GoldTriangleTeam = null;
  301 + }
  302 +
  303 + // 计算有效战队的总业绩
  304 + var teamTotalPerformance = validMembers.Sum(x => x.TotalPerformance);
  305 +
  306 + // 更新有效成员的战队业绩
  307 + foreach (var member in validMembers)
  308 + {
  309 + member.TeamPerformance = teamTotalPerformance;
  310 + }
  311 + }
  312 +
  313 + // 4. 计算薪资 (底薪 & 提成)
  314 + foreach (var salary in employeeStats.Values)
  315 + {
  316 + // 4.1 底薪计算
  317 + salary.HealthCoachBaseSalary = CalculateBaseSalary(salary.Consumption, salary.ProjectCount);
  318 +
  319 + // 4.2 提成计算
  320 + // 单人业绩 <= 6000 无提成
  321 + if (salary.TotalPerformance <= 6000)
  322 + {
  323 + salary.TotalCommission = 0;
  324 + salary.BasePerformanceCommission = 0;
  325 + salary.CooperationPerformanceCommission = 0;
  326 + salary.ConsultantCommission = 0;
  327 + }
  328 + else
  329 + {
  330 + // 确定提成点
  331 + decimal commissionPoint = 0;
  332 +
  333 + if (!string.IsNullOrEmpty(salary.GoldTriangleId))
  334 + {
  335 + // 是战队成员
  336 + // 获取战队人数 (注意:这里应该是有效战队人数)
  337 + var teamMemberCount = employeeStats.Values.Count(x => x.GoldTriangleId == salary.GoldTriangleId);
  338 + commissionPoint = GetTeamCommissionPoint(teamMemberCount, salary.TeamPerformance);
  339 + }
  340 + else
  341 + {
  342 + // 单人 (或被剔除出战队)
  343 + commissionPoint = GetTeamCommissionPoint(1, salary.TotalPerformance);
  344 + }
  345 +
  346 + salary.CommissionPoint = commissionPoint;
  347 +
  348 + // 计算基础/合作提成
  349 + salary.BasePerformanceCommission = salary.BasePerformance * 0.95m * commissionPoint;
  350 + salary.CooperationPerformanceCommission = salary.CooperationPerformance * 0.95m * 0.65m * commissionPoint;
  351 +
  352 + // 计算顾问提成
  353 + // 检查是否是顾问
  354 + var isConsultant = teamMembers.Any(x => x.UserId == salary.EmployeeId && x.IsLeader == 1);
  355 + if (isConsultant && !string.IsNullOrEmpty(salary.GoldTriangleId))
  356 + {
  357 + salary.ConsultantCommission = CalculateConsultantCommission(salary.TeamPerformance, employeeStats.Values.Where(x => x.GoldTriangleId == salary.GoldTriangleId).ToList());
  358 + }
  359 +
  360 + salary.TotalCommission = salary.BasePerformanceCommission + salary.CooperationPerformanceCommission + salary.ConsultantCommission;
  361 + }
  362 +
  363 + // 计算占比
  364 + if (salary.TeamPerformance > 0)
  365 + {
  366 + salary.Percentage = salary.TotalPerformance / salary.TeamPerformance;
  367 + }
  368 + else if (salary.TotalPerformance > 0 && string.IsNullOrEmpty(salary.GoldTriangleId))
  369 + {
  370 + salary.Percentage = 1; // 单人占比100%
  371 + }
  372 +
  373 + // 4.3 最终工资
  374 + salary.ActualSalary = salary.HealthCoachBaseSalary + salary.TotalCommission + salary.HandworkFee + salary.TotalSubsidy - salary.TotalDeduction;
  375 + }
  376 +
  377 + // 5. 保存数据
  378 + if (employeeStats.Any())
  379 + {
  380 + // 先删除当月旧数据 (防止重复)
  381 + await _db.Deleteable<LqSalaryStatisticsEntity>().Where(x => x.StatisticsMonth == monthStr).ExecuteCommandAsync();
  382 + await _db.Insertable(employeeStats.Values.ToList()).ExecuteCommandAsync();
  383 + }
  384 + }
  385 +
  386 + /// <summary>
  387 + /// 计算底薪
  388 + /// </summary>
  389 + private decimal CalculateBaseSalary(decimal consumption, decimal projectCount)
  390 + {
  391 + // 0星:<1w 或 <96个 -> 1800
  392 + // 1星:>=1w 且 >=96个 -> 2000
  393 + // 2星:>=2w 且 >=126个 -> 2200
  394 + // 3星:>=4w 且 >=156个 -> 2400
  395 +
  396 + // 特殊规则:若消耗或项目数中仅一项未达标(0星),底薪按1星(2000元)计算
  397 +
  398 + int starCons = 0;
  399 + if (consumption >= 40000) starCons = 3;
  400 + else if (consumption >= 20000) starCons = 2;
  401 + else if (consumption >= 10000) starCons = 1;
  402 +
  403 + int starProj = 0;
  404 + if (projectCount >= 156) starProj = 3;
  405 + else if (projectCount >= 126) starProj = 2;
  406 + else if (projectCount >= 96) starProj = 1;
  407 +
  408 + int finalStar = Math.Min(starCons, starProj);
  409 +
  410 + // 特殊规则处理: 仅一项未达标(0星) -> 1星
  411 + if (finalStar == 0 && (starCons > 0 || starProj > 0))
  412 + {
  413 + finalStar = 1;
  414 + }
  415 +
  416 + switch (finalStar)
  417 + {
  418 + case 3: return 2400;
  419 + case 2: return 2200;
  420 + case 1: return 2000;
  421 + default: return 1800;
  422 + }
  423 + }
  424 +
  425 + /// <summary>
  426 + /// 获取战队提成点
  427 + /// </summary>
  428 + private decimal GetTeamCommissionPoint(int memberCount, decimal teamPerformance)
  429 + {
  430 + if (memberCount >= 3)
  431 + {
  432 + if (teamPerformance >= 150000) return 0.07m;
  433 + if (teamPerformance >= 120000) return 0.06m;
  434 + if (teamPerformance >= 90000) return 0.05m;
  435 + if (teamPerformance >= 60000) return 0.04m;
  436 + if (teamPerformance >= 30000) return 0.03m;
  437 + }
  438 + else if (memberCount == 2)
  439 + {
  440 + if (teamPerformance >= 80000) return 0.06m;
  441 + if (teamPerformance >= 60000) return 0.05m;
  442 + if (teamPerformance >= 40000) return 0.04m;
  443 + if (teamPerformance >= 20000) return 0.03m;
  444 + }
  445 + else // 1人
  446 + {
  447 + if (teamPerformance >= 60000) return 0.06m;
  448 + if (teamPerformance >= 40000) return 0.05m;
  449 + if (teamPerformance >= 20000) return 0.04m;
  450 + if (teamPerformance >= 10000) return 0.03m;
  451 + }
  452 + return 0;
  453 + }
  454 +
  455 + /// <summary>
  456 + /// 计算顾问提成
  457 + /// </summary>
  458 + private decimal CalculateConsultantCommission(decimal teamPerformance, List<LqSalaryStatisticsEntity> teamMembers)
  459 + {
  460 + // 顾问提成规则:
  461 + // 高级顾问:战队总业绩 ≥ 6万元 且 组员业绩达到40%以上 且 消耗达到6万元 → 团队总业绩0.8%
  462 + // 普通顾问:战队总业绩 ≥ 4万元 且 组员业绩达到30%以上 且 消耗达到4万元 → 团队总业绩0.3%
  463 +
  464 + // 这里的“组员业绩达到X%以上”理解为:除顾问外的成员业绩占比?或者每个成员都达标?
  465 + // 通常理解为:团队中是否有成员业绩贡献较高,或者团队整体结构健康。
  466 + // 假设“组员业绩达到X%”是指:团队中至少有一名成员(非顾问本人?)或者所有成员平均?
  467 + // 鉴于规则模糊,这里先简化实现:暂只考核总业绩和消耗。
  468 + // 消耗是团队总消耗吗?假设是。
  469 +
  470 + var teamConsumption = teamMembers.Sum(x => x.Consumption);
  471 +
  472 + // 高级顾问
  473 + if (teamPerformance >= 60000 && teamConsumption >= 60000)
  474 + {
  475 + return teamPerformance * 0.008m;
  476 + }
  477 + // 普通顾问
  478 + if (teamPerformance >= 40000 && teamConsumption >= 40000)
  479 + {
  480 + return teamPerformance * 0.003m;
  481 + }
  482 +
  483 + return 0;
  484 + }
  485 + }
  486 +}
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqStatisticsService.cs
... ... @@ -2133,7 +2133,7 @@ namespace NCC.Extend.LqStatistics
2133 2133 ) personcount_stats ON jksyj.jkszh = personcount_stats.jkszh AND hyhk.md = personcount_stats.md
2134 2134 WHERE jksyj.F_IsEffective = 1
2135 2135 AND DATE_FORMAT(hyhk.hksj, '%Y%m') = @statisticsMonth
2136   - GROUP BY jksyj.jkszh, jksyj.jksxm, hyhk.md, md.mdbm, md.dm, headcount_stats.F_HeadCount, personcount_stats.F_PersonCount";
  2136 + GROUP BY jksyj.jks, jksyj.jksxm, hyhk.md, md.mdbm, md.dm, headcount_stats.F_HeadCount, personcount_stats.F_PersonCount";
2137 2137  
2138 2138 var healthCoachData = await _db.Ado.SqlQueryAsync<dynamic>(healthCoachSql, new { statisticsMonth });
2139 2139 foreach (var item in healthCoachData)
... ... @@ -4997,7 +4997,5 @@ namespace NCC.Extend.LqStatistics
4997 4997 }
4998 4998 #endregion
4999 4999  
5000   -
5001   -
5002 5000 }
5003 5001 }
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
... ... @@ -775,7 +775,7 @@ namespace NCC.Extend.LqXhHyhk
775 775 .MergeTable()
776 776 .Distinct() // 去重,因为一个耗卡可能对应多个科技部老师业绩记录
777 777 .OrderBy($"{sidx} {sort}")
778   - .ToPagedListAsync(input.currentPage, input.pageSize);
  778 + .ToPagedListAsync(input.currentPage, input.pageSize);
779 779  
780 780 // 获取当前页的耗卡记录ID列表
781 781 var consumeIds = data.list.Select(x => x.id).ToList();
... ... @@ -938,6 +938,7 @@ namespace NCC.Extend.LqXhHyhk
938 938 SourceType = item.sourceType,
939 939 IsEffective = StatusEnum.有效.GetHashCode(),
940 940 ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
  941 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "",
941 942 };
942 943 allPxmxEntities.Add(lqXhPxmxEntity);
943 944  
... ... @@ -971,7 +972,8 @@ namespace NCC.Extend.LqXhHyhk
971 972 ItemCategory = lqXhPxmxEntity.ItemCategory,
972 973 ItemId = lqXhPxmxEntity.Px,
973 974 StoreId = entity.Md,
974   - ItemName = lqXhPxmxEntity.Pxmc
  975 + ItemName = lqXhPxmxEntity.Pxmc,
  976 + PerformanceType = lqXhPxmxEntity.PerformanceType
975 977 }
976 978 );
977 979 }
... ... @@ -1010,7 +1012,8 @@ namespace NCC.Extend.LqXhHyhk
1010 1012 ItemCategory = lqXhPxmxEntity.ItemCategory,
1011 1013 ItemId = lqXhPxmxEntity.Px,
1012 1014 StoreId = entity.Md,
1013   - ItemName = lqXhPxmxEntity.Pxmc
  1015 + ItemName = lqXhPxmxEntity.Pxmc,
  1016 + PerformanceType = lqXhPxmxEntity.PerformanceType
1014 1017 }
1015 1018 );
1016 1019 }
... ... @@ -1313,6 +1316,7 @@ namespace NCC.Extend.LqXhHyhk
1313 1316 OvertimeProjectNumber = (decimal)(entity.OvertimeCoefficient * (item.projectNumber ?? 0)),
1314 1317 ProjectNumber = (decimal)((item.projectNumber ?? 0) + (entity.OvertimeCoefficient * (item.projectNumber ?? 0))),
1315 1318 ItemCategory = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Qt2).FirstAsync(),
  1319 + PerformanceType = await _db.Queryable<LqXmzlEntity>().Where(x => x.Id == item.px).Select(x => x.Fl3).FirstAsync() ?? "",
1316 1320 };
1317 1321 allPxmxEntities.Add(lqXhPxmxEntity);
1318 1322  
... ... @@ -1346,7 +1350,8 @@ namespace NCC.Extend.LqXhHyhk
1346 1350 ItemCategory = lqXhPxmxEntity.ItemCategory,
1347 1351 ItemId = lqXhPxmxEntity.Px,
1348 1352 StoreId = entity.Md,
1349   - ItemName = lqXhPxmxEntity.Pxmc
  1353 + ItemName = lqXhPxmxEntity.Pxmc,
  1354 + PerformanceType = lqXhPxmxEntity.PerformanceType
1350 1355 }
1351 1356 );
1352 1357 }
... ... @@ -1384,12 +1389,15 @@ namespace NCC.Extend.LqXhHyhk
1384 1389 ItemCategory = lqXhPxmxEntity.ItemCategory,
1385 1390 ItemId = lqXhPxmxEntity.Px,
1386 1391 StoreId = entity.Md,
1387   - ItemName = lqXhPxmxEntity.Pxmc
  1392 + ItemName = lqXhPxmxEntity.Pxmc,
  1393 + PerformanceType = lqXhPxmxEntity.PerformanceType
1388 1394 });
1389 1395 }
1390 1396 }
1391 1397 }
1392 1398 }
  1399 + //查询开单记录
  1400 + var billingRecord = await _db.Queryable<LqKdKdjlbEntity>().Where(x => x.Kdhy == entity.Hy && x.IsEffective == StatusEnum.有效.GetHashCode() && x.Sfyj > 0).AnyAsync();
1393 1401 //剔除所有T区健康师(姓名中包含"T区"的健康师)
1394 1402 var NoTallJksyjEntities = allJksyjEntities.Where(x => x.Jks != null && x.Jks != "" && (x.Jksxm == null || !x.Jksxm.Contains("T区"))).ToList();
1395 1403 //获取这次耗卡有多少个健康师
... ... @@ -1412,6 +1420,7 @@ namespace NCC.Extend.LqXhHyhk
1412 1420 Quantity = 1 / (decimal)jksCount,
1413 1421 CreateTime = DateTime.Now,
1414 1422 IsEffective = StatusEnum.有效.GetHashCode(),
  1423 + HasBilling = billingRecord ? 1 : 0,
1415 1424 };
1416 1425 allPersonTimesRecordEntities.Add(personTimesRecordEntity);
1417 1426 }
... ... @@ -1437,6 +1446,7 @@ namespace NCC.Extend.LqXhHyhk
1437 1446 Quantity = 1 / (decimal)kjbCount,
1438 1447 CreateTime = DateTime.Now,
1439 1448 IsEffective = StatusEnum.有效.GetHashCode(),
  1449 + HasBilling = billingRecord ? 1 : 0,
1440 1450 };
1441 1451 allPersonTimesRecordEntities.Add(personTimesRecordEntity);
1442 1452 }
... ...
sql/修复人次记录HasBilling字段.sql 0 → 100644
  1 +-- 修复人次记录表中F_HasBilling字段
  2 +-- 对于耗卡类型的人次记录,如果会员有开单记录(F_IsEffective=1且Sfyj>0),则设置F_HasBilling=1
  3 +
  4 +-- 1. 先查看需要修复的记录数量
  5 +SELECT COUNT(*) as need_fix_count
  6 +FROM lq_person_times_record ptr
  7 +WHERE ptr.F_BusinessType = '耗卡'
  8 + AND ptr.F_HasBilling = 0
  9 + AND ptr.F_MemberId IS NOT NULL
  10 + AND ptr.F_MemberId != ''
  11 + AND EXISTS (
  12 + SELECT 1
  13 + FROM lq_kd_kdjlb kd
  14 + WHERE kd.Kdhy = ptr.F_MemberId
  15 + AND kd.F_IsEffective = 1
  16 + AND kd.Sfyj > 0
  17 + );
  18 +
  19 +-- 2. 修复耗卡类型的人次记录
  20 +UPDATE lq_person_times_record ptr
  21 +INNER JOIN (
  22 + SELECT DISTINCT Kdhy
  23 + FROM lq_kd_kdjlb
  24 + WHERE F_IsEffective = 1
  25 + AND Sfyj > 0
  26 +) kd ON ptr.F_MemberId = kd.Kdhy
  27 +SET ptr.F_HasBilling = 1
  28 +WHERE ptr.F_BusinessType = '耗卡'
  29 + AND ptr.F_HasBilling = 0
  30 + AND ptr.F_MemberId IS NOT NULL
  31 + AND ptr.F_MemberId != '';
  32 +
  33 +-- 3. 验证修复结果(可选)
  34 +SELECT COUNT(*) as fixed_count
  35 +FROM lq_person_times_record ptr
  36 +WHERE ptr.F_BusinessType = '耗卡'
  37 + AND ptr.F_HasBilling = 1
  38 + AND ptr.F_MemberId IS NOT NULL
  39 + AND ptr.F_MemberId != ''
  40 + AND EXISTS (
  41 + SELECT 1
  42 + FROM lq_kd_kdjlb kd
  43 + WHERE kd.Kdhy = ptr.F_MemberId
  44 + AND kd.F_IsEffective = 1
  45 + AND kd.Sfyj > 0
  46 + );
  47 +
... ...
sql/同步退卡时间字段.sql 0 → 100644
  1 +-- 同步退卡业绩表的退卡时间到明细表和业绩表
  2 +-- 数据来源:lq_hytk_hytk.tksj(退卡时间)
  3 +
  4 +-- ============================================
  5 +-- 1. 同步退卡明细表(lq_hytk_mx)的退卡时间
  6 +-- ============================================
  7 +-- 通过 F_RefundInfoId 关联到退卡业绩表的 F_Id
  8 +UPDATE lq_hytk_mx mx
  9 +INNER JOIN lq_hytk_hytk hytk ON mx.F_RefundInfoId = hytk.F_Id
  10 +SET mx.tksj = hytk.tksj
  11 +WHERE hytk.tksj IS NOT NULL
  12 + AND (mx.tksj IS NULL OR mx.tksj != hytk.tksj);
  13 +
  14 +-- ============================================
  15 +-- 2. 同步退卡健康师业绩表(lq_hytk_jksyj)的退卡时间
  16 +-- ============================================
  17 +-- 通过 gltkbh 关联到退卡业绩表的 F_Id
  18 +UPDATE lq_hytk_jksyj jksyj
  19 +INNER JOIN lq_hytk_hytk hytk ON jksyj.gltkbh = hytk.F_Id
  20 +SET jksyj.tksj = hytk.tksj
  21 +WHERE hytk.tksj IS NOT NULL
  22 + AND (jksyj.tksj IS NULL OR jksyj.tksj != hytk.tksj);
  23 +
  24 +-- ============================================
  25 +-- 3. 同步退卡科技老师业绩表(lq_hytk_kjbsyj)的退卡时间
  26 +-- ============================================
  27 +-- 通过 gltkbh 关联到退卡业绩表的 F_Id
  28 +UPDATE lq_hytk_kjbsyj kjbsyj
  29 +INNER JOIN lq_hytk_hytk hytk ON kjbsyj.gltkbh = hytk.F_Id
  30 +SET kjbsyj.tksj = hytk.tksj
  31 +WHERE hytk.tksj IS NOT NULL
  32 + AND (kjbsyj.tksj IS NULL OR kjbsyj.tksj != hytk.tksj);
  33 +
... ...
sql/更新历史数据业绩类型字段.sql 0 → 100644
  1 +-- 更新历史数据的业绩类型字段
  2 +-- 业绩类型根据品项表(lq_xmzl)的 fl3 字段来设置
  3 +-- fl3 字段是"分类③-工资用",用于区分不同的业绩类型
  4 +
  5 +-- 1. 更新开单品项表(通过品项ID关联品项表的fl3字段)
  6 +UPDATE lq_kd_pxmx pxmx
  7 +INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id
  8 +SET pxmx.F_PerformanceType = COALESCE(xmzl.fl3, '')
  9 +WHERE (pxmx.F_PerformanceType IS NULL OR pxmx.F_PerformanceType = '')
  10 + AND pxmx.F_IsEffective = 1;
  11 +
  12 +-- 2. 更新开单健康师业绩表(通过品项ID或开单品项明细ID关联品项表的fl3字段)
  13 +UPDATE lq_kd_jksyj jksyj
  14 +LEFT JOIN lq_kd_pxmx pxmx ON jksyj.F_kdpxid = pxmx.F_Id
  15 +LEFT JOIN lq_xmzl xmzl ON COALESCE(jksyj.F_ItemId, pxmx.px) = xmzl.F_Id
  16 +SET jksyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  17 +WHERE (jksyj.F_PerformanceType IS NULL OR jksyj.F_PerformanceType = '')
  18 + AND jksyj.F_IsEffective = 1
  19 + AND xmzl.F_Id IS NOT NULL;
  20 +
  21 +-- 3. 更新开单科技部老师业绩表(通过品项ID或开单品项明细ID关联品项表的fl3字段)
  22 +UPDATE lq_kd_kjbsyj kjbsyj
  23 +LEFT JOIN lq_kd_pxmx pxmx ON kjbsyj.F_kdpxid = pxmx.F_Id
  24 +LEFT JOIN lq_xmzl xmzl ON COALESCE(kjbsyj.F_ItemId, pxmx.px) = xmzl.F_Id
  25 +SET kjbsyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  26 +WHERE (kjbsyj.F_PerformanceType IS NULL OR kjbsyj.F_PerformanceType = '')
  27 + AND kjbsyj.F_IsEffective = 1
  28 + AND xmzl.F_Id IS NOT NULL;
  29 +
  30 +-- 4. 更新消耗品项表(通过品项ID关联品项表的fl3字段)
  31 +UPDATE lq_xh_pxmx pxmx
  32 +INNER JOIN lq_xmzl xmzl ON pxmx.px = xmzl.F_Id
  33 +SET pxmx.F_PerformanceType = COALESCE(xmzl.fl3, '')
  34 +WHERE (pxmx.F_PerformanceType IS NULL OR pxmx.F_PerformanceType = '')
  35 + AND (pxmx.F_IsEffective = 1 OR pxmx.F_IsEffective IS NULL);
  36 +
  37 +-- 5. 更新消耗健康师业绩表(通过品项ID关联品项表的fl3字段)
  38 +UPDATE lq_xh_jksyj jksyj
  39 +LEFT JOIN lq_xh_pxmx pxmx ON jksyj.F_kdpxid = pxmx.F_Id
  40 +LEFT JOIN lq_xmzl xmzl ON COALESCE(jksyj.F_ItemId, pxmx.px) = xmzl.F_Id
  41 +SET jksyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  42 +WHERE (jksyj.F_PerformanceType IS NULL OR jksyj.F_PerformanceType = '')
  43 + AND (jksyj.F_IsEffective = 1 OR jksyj.F_IsEffective IS NULL)
  44 + AND xmzl.F_Id IS NOT NULL;
  45 +
  46 +-- 6. 更新消耗科技部老师业绩表(通过品项ID关联品项表的fl3字段)
  47 +UPDATE lq_xh_kjbsyj kjbsyj
  48 +LEFT JOIN lq_xh_pxmx pxmx ON kjbsyj.F_hkpxid = pxmx.F_Id
  49 +LEFT JOIN lq_xmzl xmzl ON COALESCE(kjbsyj.F_ItemId, pxmx.px) = xmzl.F_Id
  50 +SET kjbsyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  51 +WHERE (kjbsyj.F_PerformanceType IS NULL OR kjbsyj.F_PerformanceType = '')
  52 + AND (kjbsyj.F_IsEffective = 1 OR kjbsyj.F_IsEffective IS NULL)
  53 + AND xmzl.F_Id IS NOT NULL;
  54 +
  55 +-- 7. 更新退卡明细表(通过品项ID关联品项表的fl3字段)
  56 +UPDATE lq_hytk_mx mx
  57 +INNER JOIN lq_xmzl xmzl ON mx.px = xmzl.F_Id
  58 +SET mx.F_PerformanceType = COALESCE(xmzl.fl3, '')
  59 +WHERE (mx.F_PerformanceType IS NULL OR mx.F_PerformanceType = '')
  60 + AND mx.F_IsEffective = 1;
  61 +
  62 +-- 8. 更新退卡健康师业绩表(通过品项ID关联品项表的fl3字段)
  63 +UPDATE lq_hytk_jksyj jksyj
  64 +LEFT JOIN lq_hytk_mx mx ON jksyj.F_CardReturn = mx.F_Id
  65 +LEFT JOIN lq_xmzl xmzl ON COALESCE(jksyj.F_ItemId, mx.px) = xmzl.F_Id
  66 +SET jksyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  67 +WHERE (jksyj.F_PerformanceType IS NULL OR jksyj.F_PerformanceType = '')
  68 + AND jksyj.F_IsEffective = 1
  69 + AND xmzl.F_Id IS NOT NULL;
  70 +
  71 +-- 9. 更新退卡科技部老师业绩表(通过品项ID关联品项表的fl3字段)
  72 +UPDATE lq_hytk_kjbsyj kjbsyj
  73 +LEFT JOIN lq_hytk_mx mx ON kjbsyj.F_CardReturn = mx.F_Id
  74 +LEFT JOIN lq_xmzl xmzl ON COALESCE(kjbsyj.F_ItemId, mx.px) = xmzl.F_Id
  75 +SET kjbsyj.F_PerformanceType = COALESCE(xmzl.fl3, '')
  76 +WHERE (kjbsyj.F_PerformanceType IS NULL OR kjbsyj.F_PerformanceType = '')
  77 + AND kjbsyj.F_IsEffective = 1
  78 + AND xmzl.F_Id IS NOT NULL;
  79 +
  80 +-- 10. 更新业绩明细表(lq_yjmxb)
  81 +-- 业绩明细表通常是汇总表,可能包含开单和消耗的数据
  82 +-- 由于无法准确判断每条记录的具体来源,这里先设置为空
  83 +-- 如果需要更精确的更新,可以根据业务逻辑关联其他表来判断
  84 +-- 例如:如果业绩明细表有开单编号字段,可以关联开单表判断为'开单'
  85 +-- 如果业绩明细表有耗卡编号字段,可以关联耗卡表判断为'消耗'
  86 +UPDATE lq_yjmxb
  87 +SET F_PerformanceType = NULL
  88 +WHERE F_PerformanceType IS NULL OR F_PerformanceType = '';
  89 +
  90 +-- 注意:如果业绩明细表有明确的关联字段,可以使用以下逻辑进行更精确的更新:
  91 +-- UPDATE lq_yjmxb yj
  92 +-- LEFT JOIN lq_kd_kdjlb kd ON yj.关联字段 = kd.F_Id
  93 +-- LEFT JOIN lq_xh_hyhk xh ON yj.关联字段 = xh.F_Id
  94 +-- SET yj.F_PerformanceType = CASE
  95 +-- WHEN kd.F_Id IS NOT NULL THEN '开单'
  96 +-- WHEN xh.F_Id IS NOT NULL THEN '消耗'
  97 +-- ELSE NULL
  98 +-- END
  99 +-- WHERE yj.F_PerformanceType IS NULL OR yj.F_PerformanceType = '';
  100 +
  101 +-- 验证更新结果(统计各表的业绩类型分布)
  102 +SELECT
  103 + 'lq_kd_pxmx' as table_name,
  104 + COUNT(*) as total_count,
  105 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  106 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  107 +FROM lq_kd_pxmx
  108 +WHERE F_IsEffective = 1
  109 +UNION ALL
  110 +SELECT
  111 + 'lq_kd_jksyj' as table_name,
  112 + COUNT(*) as total_count,
  113 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  114 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  115 +FROM lq_kd_jksyj
  116 +WHERE F_IsEffective = 1
  117 +UNION ALL
  118 +SELECT
  119 + 'lq_kd_kjbsyj' as table_name,
  120 + COUNT(*) as total_count,
  121 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  122 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  123 +FROM lq_kd_kjbsyj
  124 +WHERE F_IsEffective = 1
  125 +UNION ALL
  126 +SELECT
  127 + 'lq_xh_pxmx' as table_name,
  128 + COUNT(*) as total_count,
  129 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  130 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  131 +FROM lq_xh_pxmx
  132 +WHERE F_IsEffective = 1 OR F_IsEffective IS NULL
  133 +UNION ALL
  134 +SELECT
  135 + 'lq_xh_jksyj' as table_name,
  136 + COUNT(*) as total_count,
  137 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  138 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  139 +FROM lq_xh_jksyj
  140 +WHERE F_IsEffective = 1 OR F_IsEffective IS NULL
  141 +UNION ALL
  142 +SELECT
  143 + 'lq_xh_kjbsyj' as table_name,
  144 + COUNT(*) as total_count,
  145 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  146 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  147 +FROM lq_xh_kjbsyj
  148 +WHERE F_IsEffective = 1 OR F_IsEffective IS NULL
  149 +UNION ALL
  150 +SELECT
  151 + 'lq_hytk_mx' as table_name,
  152 + COUNT(*) as total_count,
  153 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  154 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  155 +FROM lq_hytk_mx
  156 +WHERE F_IsEffective = 1
  157 +UNION ALL
  158 +SELECT
  159 + 'lq_hytk_jksyj' as table_name,
  160 + COUNT(*) as total_count,
  161 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  162 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  163 +FROM lq_hytk_jksyj
  164 +WHERE F_IsEffective = 1
  165 +UNION ALL
  166 +SELECT
  167 + 'lq_hytk_kjbsyj' as table_name,
  168 + COUNT(*) as total_count,
  169 + SUM(CASE WHEN F_PerformanceType IS NOT NULL AND F_PerformanceType != '' THEN 1 ELSE 0 END) as '已设置',
  170 + SUM(CASE WHEN F_PerformanceType IS NULL OR F_PerformanceType = '' THEN 1 ELSE 0 END) as '未设置'
  171 +FROM lq_hytk_kjbsyj
  172 +WHERE F_IsEffective = 1;
  173 +
  174 +-- 查看业绩类型的值分布(从品项表fl3字段)
  175 +SELECT
  176 + 'lq_xmzl.fl3 值分布' as description,
  177 + fl3 as performance_type,
  178 + COUNT(*) as count
  179 +FROM lq_xmzl
  180 +WHERE fl3 IS NOT NULL AND fl3 != ''
  181 +GROUP BY fl3
  182 +ORDER BY count DESC;
  183 +
... ...
sql/更新开单记录表储扣金额.sql 0 → 100644
  1 +-- 更新开单记录表中的储扣金额,使其等于储扣详情表中汇总的金额
  2 +-- 只更新那些储扣金额不一致的记录(包括NULL、0或金额不匹配的情况)
  3 +
  4 +UPDATE lq_kd_kdjlb kd
  5 +INNER JOIN (
  6 + SELECT
  7 + F_BillingId,
  8 + COALESCE(SUM(F_Amount), 0) as total_amount
  9 + FROM lq_kd_deductinfo
  10 + WHERE F_IsEffective = 1
  11 + GROUP BY F_BillingId
  12 +) deduct_sum ON kd.F_Id = deduct_sum.F_BillingId
  13 +SET kd.F_DeductAmount = deduct_sum.total_amount
  14 +WHERE kd.F_IsEffective = 1
  15 + AND (
  16 + kd.F_DeductAmount IS NULL
  17 + OR kd.F_DeductAmount = 0
  18 + OR ABS(kd.F_DeductAmount - deduct_sum.total_amount) > 0.01
  19 + )
  20 + AND deduct_sum.total_amount > 0;
  21 +
... ...
sql/添加业绩类型字段.sql 0 → 100644
  1 +-- 添加业绩类型字段到相关表
  2 +-- 字段名:F_PerformanceType
  3 +-- 字段类型:varchar(50)
  4 +-- 字段说明:业绩类型
  5 +
  6 +-- 1. 开单品项表
  7 +ALTER TABLE lq_kd_pxmx
  8 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER F_ActivityId;
  9 +
  10 +-- 2. 开单健康师业绩表
  11 +ALTER TABLE lq_kd_jksyj
  12 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER F_ItemName;
  13 +
  14 +-- 3. 开单科技部老师业绩表
  15 +ALTER TABLE lq_kd_kjbsyj
  16 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER F_ItemName;
  17 +
  18 +-- 4. 消耗品项表
  19 +ALTER TABLE lq_xh_pxmx
  20 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER F_IsEffective;
  21 +
  22 +-- 5. 消耗健康师业绩表
  23 +ALTER TABLE lq_xh_jksyj
  24 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER F_ItemName;
  25 +
  26 +-- 6. 消耗科技部老师业绩表
  27 +ALTER TABLE lq_xh_kjbsyj
  28 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER F_ItemName;
  29 +
  30 +-- 7. 退卡明细表
  31 +ALTER TABLE lq_hytk_mx
  32 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER F_IsEffective;
  33 +
  34 +-- 8. 退卡健康师业绩表
  35 +ALTER TABLE lq_hytk_jksyj
  36 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER F_ItemName;
  37 +
  38 +-- 9. 退卡科技部老师业绩表
  39 +ALTER TABLE lq_hytk_kjbsyj
  40 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER F_ItemName;
  41 +
  42 +-- 10. 业绩明细表
  43 +ALTER TABLE lq_yjmxb
  44 +ADD COLUMN F_PerformanceType VARCHAR(50) NULL COMMENT '业绩类型' AFTER zyj;
  45 +
... ...
项目信息-薪酬规则与名词解释.md
... ... @@ -124,8 +124,8 @@
124 124  
125 125 ### 顾问提成规则
126 126  
127   -- **高级顾问**:战队总业绩 ≥ 6万元 且 组员业绩达到40%以上 且 消耗达到6万元 → 团队总业绩0.8%
128   -- **普通顾问**:战队总业绩 ≥ 4万元 且 组员业绩达到30%以上 且 消耗达到4万元 → 团队总业绩0.3%
  127 +- 战队总业绩 ≥ 6万元 且 组员业绩达到40%以上 且 消耗达到6万元 → 团队总业绩0.8%
  128 +- 战队总业绩 ≥ 4万元 且 组员业绩达到30%以上 且 消耗达到4万元 → 团队总业绩0.3%
129 129  
130 130 ### 店助考核规则
131 131  
... ...