educode_sales 0.9.83 → 0.9.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/educode_sales/contracts_controller.rb +5 -3
- data/app/controllers/educode_sales/sale_trends_controller.rb +1 -1
- data/app/controllers/educode_sales/sales_details_controller.rb +6 -0
- data/app/helpers/educode_sales/sale_trends_helper.rb +225 -135
- data/app/models/educode_sales/follow_up.rb +2 -0
- data/app/models/educode_sales/sale_trend.rb +3 -3
- data/app/models/educode_sales/sales_detail.rb +7 -0
- data/app/services/return_forecast_service.rb +230 -0
- data/app/views/educode_sales/contracts/new_follow_up.html.erb +9 -3
- data/app/views/educode_sales/sale_trends/_goal_forecast.html.erb +1 -1
- data/app/views/educode_sales/sales_details/index.html.erb +0 -0
- data/app/views/layouts/educode_sales/application.html.erb +2 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20230510144317_create_educode_sales_sales_details.rb +16 -0
- data/lib/educode_sales/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e498fa041334cb4b387425a7a7f7ff49dffffcee5a72e363b61cb9f32167ff06
|
4
|
+
data.tar.gz: 54d79d72be99cab109f9b40fdd4dd789f72f20d115663abd7d99db70f0ee2527
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e29ad8d3c8dd8c0753dc6662e26c332444cbf10842d308977883c47149c676b0b1c06c515bcfc33e10fe0bfccbc1cecb5ab1c7f2534001c0c1d7a11042a5def
|
7
|
+
data.tar.gz: 5077f36f7bb732c196608fd15c696c06b3d0cf5c74e32c24495b67eadc8b5527eacb1a545966aeea4e3401410ed0e1fbe23c2fb60aea58a8b5513fd1fd570ba6
|
@@ -233,12 +233,14 @@ module EducodeSales
|
|
233
233
|
@business = Business.find(params[:id])
|
234
234
|
@last_follow_up = @business.last_follow_up
|
235
235
|
@deployment_type = EducodeSales::FollowUp::BUSINESS_DEPLOYMENT
|
236
|
+
@signed_clazz = EducodeSales::FollowUp.signed_clazzs.keys
|
237
|
+
|
236
238
|
signed_department = @last_follow_up.signed_department
|
237
239
|
if signed_department
|
238
|
-
gon.department = {value: signed_department.id, name: "#{signed_department&.school&.name}-#{signed_department&.name}"}
|
240
|
+
gon.department = [{value: signed_department.id, name: "#{signed_department&.school&.name}-#{signed_department&.name}"}]
|
239
241
|
gon.value = signed_department.id
|
240
242
|
else
|
241
|
-
gon.department =
|
243
|
+
gon.department = []
|
242
244
|
gon.value = ''
|
243
245
|
end
|
244
246
|
|
@@ -454,7 +456,7 @@ module EducodeSales
|
|
454
456
|
|
455
457
|
private
|
456
458
|
def follow_up_params
|
457
|
-
params.permit(:o_business_deployment, :plan_signed_date, :bidded_days, :signed_date, :signed_department_id, :service_years, :service_start_time, :service_end_time, :funding_source)
|
459
|
+
params.permit(:o_business_deployment, :plan_signed_date, :bidded_days, :signed_date, :signed_department_id, :service_years, :service_start_time, :service_end_time, :funding_source, :signed_clazz)
|
458
460
|
end
|
459
461
|
|
460
462
|
def product_up_params
|
@@ -391,7 +391,7 @@ module EducodeSales
|
|
391
391
|
staff_id = params[:staff_id] || nil
|
392
392
|
@forecast_count_range = params[:forecast_count_range] || "month"
|
393
393
|
forecast_default_dates = ("#{Time.now.year}-01-01".to_date.."#{Time.now.year}-#{Time.now.month}-01".to_date).map { |d| d.strftime("%Y-%m") }.uniq
|
394
|
-
sale_names = %w[计划投标额 实际中标额]
|
394
|
+
sale_names = %w[计划投标额 计划投标额累计 实际中标额 实际中标额累计]
|
395
395
|
case @forecast_count_range
|
396
396
|
when "week"
|
397
397
|
# 年初第一天周数是0会导致周数重复计算
|
@@ -1,45 +1,58 @@
|
|
1
1
|
module EducodeSales
|
2
2
|
module SaleTrendsHelper
|
3
|
-
|
3
|
+
NAMES = %w[计划投标额 计划投标额累计 实际中标额 实际中标额累计]
|
4
4
|
def goal_forecast_quarter(labels, selects, staff_id = nil)
|
5
5
|
plan_get = plan_get(staff_id, labels, "quarter")
|
6
6
|
actual_get = actual_get(staff_id, labels, "quarter")
|
7
|
-
datasets = selects.map do |select|
|
8
|
-
if select ==
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
7
|
+
datasets = selects.map.with_index do |select, i|
|
8
|
+
data = if select == NAMES[0]
|
9
|
+
labels.map do |d|
|
10
|
+
quarter = d.split("-").last.to_i
|
11
|
+
year = d.split("-").first.to_i
|
12
|
+
plan_get[[year, quarter]]&.pluck(:budget_amount)&.reject(&:blank?)&.sum.to_f
|
13
|
+
end
|
14
|
+
elsif select == NAMES[1]
|
15
|
+
arr = labels.map do |d|
|
16
|
+
quarter = d.split("-").last.to_i
|
17
|
+
year = d.split("-").first.to_i
|
18
|
+
plan_get[[year, quarter]]&.pluck(:budget_amount)&.reject(&:blank?)&.sum.to_f
|
19
|
+
end
|
20
|
+
arr.map.with_index(1) { |num, i| arr.first(i).inject(:+) }
|
21
|
+
elsif select == NAMES[2]
|
22
|
+
labels.map do |d|
|
23
|
+
quarter = d.split("-").last.to_i
|
24
|
+
year = d.split("-").first.to_i
|
25
|
+
actual_get[[year, quarter]]&.pluck(:actual_amount)&.reject(&:blank?)&.sum.to_f
|
26
|
+
end
|
27
|
+
elsif select == NAMES[3]
|
28
|
+
arr = labels.map do |d|
|
29
|
+
quarter = d.split("-").last.to_i
|
30
|
+
year = d.split("-").first.to_i
|
31
|
+
actual_get[[year, quarter]]&.pluck(:actual_amount)&.reject(&:blank?)&.sum.to_f
|
32
|
+
end
|
33
|
+
arr.map.with_index(1) { |num, i| arr.first(i).inject(:+) }
|
34
|
+
end
|
35
|
+
type = if select == NAMES[3] || select == NAMES[1]
|
36
|
+
'line'
|
37
|
+
else
|
38
|
+
"bar"
|
39
|
+
end
|
40
|
+
backgroundColor = if select == NAMES[3] || select == NAMES[1]
|
41
|
+
'rgba(54, 162, 235, 0.2)'
|
42
|
+
else
|
43
|
+
SaleTrend::COLORS[i]
|
44
|
+
end
|
45
|
+
{
|
46
|
+
type: type,
|
47
|
+
label: select,
|
48
|
+
data: data,
|
49
|
+
backgroundColor:
|
50
|
+
backgroundColor,
|
51
|
+
borderColor:
|
52
|
+
SaleTrend::COLORS[i],
|
53
|
+
borderWidth: 1
|
54
|
+
}
|
41
55
|
end
|
42
|
-
|
43
56
|
{
|
44
57
|
labels: labels,
|
45
58
|
datasets: datasets
|
@@ -50,42 +63,58 @@ module EducodeSales
|
|
50
63
|
|
51
64
|
plan_get = plan_get(staff_id, labels, "week")
|
52
65
|
actual_get = actual_get(staff_id, labels, "week")
|
53
|
-
datasets = selects.map do |select|
|
54
|
-
if select ==
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
66
|
+
datasets = selects.map.with_index do |select, i|
|
67
|
+
data = if select == NAMES[0]
|
68
|
+
labels.map do |d|
|
69
|
+
week = d.split("-").last
|
70
|
+
year = d.split("-").first
|
71
|
+
yearweek = "#{year}#{week}".to_i
|
72
|
+
plan_get[yearweek].to_f
|
73
|
+
end
|
74
|
+
elsif select == NAMES[1]
|
75
|
+
arr = labels.map do |d|
|
76
|
+
week = d.split("-").last
|
77
|
+
year = d.split("-").first
|
78
|
+
yearweek = "#{year}#{week}".to_i
|
79
|
+
plan_get[yearweek].to_f
|
80
|
+
end
|
81
|
+
arr.map.with_index(1) { |num, i| arr.first(i).inject(:+) }
|
82
|
+
elsif select == NAMES[2]
|
83
|
+
labels.map do |d|
|
84
|
+
week = d.split("-").last
|
85
|
+
year = d.split("-").first
|
86
|
+
yearweek = "#{year}#{week}".to_i
|
87
|
+
actual_get[yearweek].to_f
|
88
|
+
end
|
89
|
+
elsif select == NAMES[3]
|
90
|
+
arr = labels.map do |d|
|
91
|
+
week = d.split("-").last
|
92
|
+
year = d.split("-").first
|
93
|
+
yearweek = "#{year}#{week}".to_i
|
94
|
+
actual_get[yearweek].to_f
|
95
|
+
end
|
96
|
+
arr.map.with_index(1) { |num, i| arr.first(i).inject(:+) }
|
97
|
+
end
|
98
|
+
type = if select == NAMES[0] || select == NAMES[2]
|
99
|
+
'bar'
|
100
|
+
else
|
101
|
+
"line"
|
102
|
+
end
|
103
|
+
backgroundColor = if select == NAMES[0] || select == NAMES[2]
|
104
|
+
SaleTrend::COLORS[i]
|
105
|
+
else
|
106
|
+
'rgba(54, 162, 235, 0.2)'
|
107
|
+
end
|
108
|
+
{
|
109
|
+
type: type,
|
110
|
+
label: select,
|
111
|
+
data: data,
|
112
|
+
backgroundColor:
|
113
|
+
backgroundColor,
|
114
|
+
borderColor:
|
115
|
+
SaleTrend::COLORS[i],
|
116
|
+
borderWidth: 1
|
117
|
+
}
|
89
118
|
end
|
90
119
|
|
91
120
|
{
|
@@ -97,40 +126,55 @@ module EducodeSales
|
|
97
126
|
def goal_forecast_month(labels, selects, staff_id = nil)
|
98
127
|
plan_get = plan_get(staff_id, labels, "month")
|
99
128
|
actual_get = actual_get(staff_id, labels, "month")
|
100
|
-
datasets = selects.map do |select|
|
101
|
-
if select ==
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
129
|
+
datasets = selects.map.with_index do |select, i|
|
130
|
+
data = if select == NAMES[0]
|
131
|
+
labels.map do |d|
|
132
|
+
month = d.split("-").last.to_i
|
133
|
+
year = d.split("-").first.to_i
|
134
|
+
plan_get[[year, month]]&.pluck(:budget_amount)&.reject(&:blank?)&.sum.to_f
|
135
|
+
end
|
136
|
+
elsif select == NAMES[1]
|
137
|
+
arr = labels.map do |d|
|
138
|
+
month = d.split("-").last.to_i
|
139
|
+
year = d.split("-").first.to_i
|
140
|
+
plan_get[[year, month]]&.pluck(:budget_amount)&.reject(&:blank?)&.sum.to_f
|
141
|
+
end
|
142
|
+
arr.map.with_index(1) { |num, i| arr.first(i).inject(:+) }
|
143
|
+
elsif select == NAMES[2]
|
144
|
+
labels.map do |d|
|
145
|
+
month = d.split("-").last.to_i
|
146
|
+
year = d.split("-").first.to_i
|
147
|
+
actual_get[[year, month]]&.pluck(:actual_amount)&.reject(&:blank?)&.sum.to_f
|
148
|
+
end
|
149
|
+
elsif select == NAMES[3]
|
150
|
+
arr = labels.map do |d|
|
151
|
+
month = d.split("-").last.to_i
|
152
|
+
year = d.split("-").first.to_i
|
153
|
+
actual_get[[year, month]]&.pluck(:actual_amount)&.reject(&:blank?)&.sum.to_f
|
154
|
+
end
|
155
|
+
arr.map.with_index(1) { |num, i| arr.first(i).inject(:+) }
|
156
|
+
end
|
157
|
+
|
158
|
+
type = if select == NAMES[3] || select == NAMES[1]
|
159
|
+
'line'
|
160
|
+
else
|
161
|
+
"bar"
|
162
|
+
end
|
163
|
+
backgroundColor = if select == NAMES[3] || select == NAMES[1]
|
164
|
+
'rgba(54, 162, 235, 0.2)'
|
165
|
+
else
|
166
|
+
SaleTrend::COLORS[i]
|
167
|
+
end
|
168
|
+
{
|
169
|
+
type: type,
|
170
|
+
label: select,
|
171
|
+
data: data,
|
172
|
+
backgroundColor:
|
173
|
+
backgroundColor,
|
174
|
+
borderColor:
|
175
|
+
SaleTrend::COLORS[i],
|
176
|
+
borderWidth: 1
|
177
|
+
}
|
134
178
|
end
|
135
179
|
|
136
180
|
{
|
@@ -142,34 +186,40 @@ module EducodeSales
|
|
142
186
|
def goal_forecast_year(labels, selects, staff_id = nil)
|
143
187
|
plan_get = plan_get(staff_id, labels, "year")
|
144
188
|
actual_get = actual_get(staff_id, labels, "year")
|
145
|
-
datasets = selects.map do |select|
|
146
|
-
if select ==
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
}
|
158
|
-
else
|
159
|
-
{
|
160
|
-
label: select,
|
161
|
-
data: labels.map { |d| actual_get[d.to_i] || 0 },
|
162
|
-
backgroundColor: [
|
163
|
-
'rgba(54, 162, 235, 0.2)',
|
164
|
-
],
|
165
|
-
borderColor: [
|
166
|
-
'rgba(54, 162, 235, 1)',
|
167
|
-
],
|
168
|
-
borderWidth: 1
|
169
|
-
}
|
170
|
-
end
|
171
|
-
end
|
189
|
+
datasets = selects.map.with_index do |select, i|
|
190
|
+
data = if select == NAMES[0]
|
191
|
+
labels.map { |d| plan_get[d.to_i] || 0 }
|
192
|
+
elsif select == NAMES[1]
|
193
|
+
arr = labels.map { |d| plan_get[d.to_i] || 0 }
|
194
|
+
arr.map.with_index(1) { |num, i| arr.first(i).inject(:+) }
|
195
|
+
elsif select == NAMES[2]
|
196
|
+
labels.map { |d| actual_get[d.to_i] || 0 }
|
197
|
+
elsif select == NAMES[3]
|
198
|
+
arr = labels.map { |d| actual_get[d.to_i] || 0 }
|
199
|
+
arr.map.with_index(1) { |num, i| arr.first(i).inject(:+) }
|
200
|
+
end
|
172
201
|
|
202
|
+
type = if select == NAMES[3] || select == NAMES[1]
|
203
|
+
'line'
|
204
|
+
else
|
205
|
+
"bar"
|
206
|
+
end
|
207
|
+
backgroundColor = if select == NAMES[3] || select == NAMES[1]
|
208
|
+
'rgba(54, 162, 235, 0.2)'
|
209
|
+
else
|
210
|
+
SaleTrend::COLORS[i]
|
211
|
+
end
|
212
|
+
{
|
213
|
+
type: type,
|
214
|
+
label: select,
|
215
|
+
data: data,
|
216
|
+
backgroundColor:
|
217
|
+
backgroundColor,
|
218
|
+
borderColor:
|
219
|
+
SaleTrend::COLORS[i],
|
220
|
+
borderWidth: 1
|
221
|
+
}
|
222
|
+
end
|
173
223
|
{
|
174
224
|
labels: labels,
|
175
225
|
datasets: datasets
|
@@ -180,9 +230,29 @@ module EducodeSales
|
|
180
230
|
|
181
231
|
def plan_get(staff_id, time_range, type)
|
182
232
|
# budget_amount 预算额
|
233
|
+
start_time = case type
|
234
|
+
when "month"
|
235
|
+
time_range.first + "-01"
|
236
|
+
when "year"
|
237
|
+
time_range.first + "-01-01"
|
238
|
+
when "week"
|
239
|
+
time_range.first.split("-").first + "-01-01"
|
240
|
+
when "quarter"
|
241
|
+
time_range.first.split("-").first + "-01-01"
|
242
|
+
end
|
243
|
+
end_time = case type
|
244
|
+
when "month"
|
245
|
+
time_range.last + "-31"
|
246
|
+
when "year"
|
247
|
+
time_range.last + "-12-31"
|
248
|
+
when "week"
|
249
|
+
time_range.last.split("-").first + "-12-31"
|
250
|
+
when "quarter"
|
251
|
+
time_range.last.split("-").first + "-12-31"
|
252
|
+
end
|
183
253
|
staff_id = staff_id.present? ? Array(staff_id) : nil
|
184
254
|
data = Business.joins(:last_follow_up)
|
185
|
-
.where("educode_sales_follow_ups.invitation_at >= ? and educode_sales_follow_ups.invitation_at <= ? #{staff_id.present? ? "AND educode_sales_follow_ups.staff_id in (#{staff_id.join(",")})" : ""}",
|
255
|
+
.where("educode_sales_follow_ups.invitation_at >= ? and educode_sales_follow_ups.invitation_at <= ? #{staff_id.present? ? "AND educode_sales_follow_ups.staff_id in (#{staff_id.join(",")})" : ""}", start_time, end_time)
|
186
256
|
case type
|
187
257
|
when "week"
|
188
258
|
data.select("educode_sales_follow_ups.*, yearweek(educode_sales_follow_ups.invitation_at) as week").group("yearweek(educode_sales_follow_ups.invitation_at)").sum(:budget_amount)
|
@@ -198,10 +268,30 @@ module EducodeSales
|
|
198
268
|
end
|
199
269
|
|
200
270
|
def actual_get(staff_id, time_range, type)
|
271
|
+
start_time = case type
|
272
|
+
when "month"
|
273
|
+
time_range.first + "-01"
|
274
|
+
when "year"
|
275
|
+
time_range.first + "-01-01"
|
276
|
+
when "week"
|
277
|
+
time_range.first.split("-").first + "-01-01"
|
278
|
+
when "quarter"
|
279
|
+
time_range.first.split("-").first + "-01-01"
|
280
|
+
end
|
281
|
+
end_time = case type
|
282
|
+
when "month"
|
283
|
+
time_range.last + "-31"
|
284
|
+
when "year"
|
285
|
+
time_range.last + "-12-31"
|
286
|
+
when "week"
|
287
|
+
time_range.last.split("-").first + "-12-31"
|
288
|
+
when "quarter"
|
289
|
+
time_range.last.split("-").first + "-12-31"
|
290
|
+
end
|
201
291
|
# actual_amount 合同额
|
202
292
|
staff_id = staff_id.present? ? Array(staff_id) : nil
|
203
293
|
data = Business.joins(:last_follow_up)
|
204
|
-
.where("educode_sales_follow_ups.bidded_date >= ? and educode_sales_follow_ups.bidded_date <= ? #{staff_id.present? ? "AND educode_sales_follow_ups.staff_id in (#{staff_id.join(",")})" : ""}",
|
294
|
+
.where("educode_sales_follow_ups.bidded_date >= ? and educode_sales_follow_ups.bidded_date <= ? #{staff_id.present? ? "AND educode_sales_follow_ups.staff_id in (#{staff_id.join(",")})" : ""}", start_time, end_time)
|
205
295
|
case type
|
206
296
|
when "week"
|
207
297
|
data.select("educode_sales_follow_ups.*, yearweek(educode_sales_follow_ups.bidded_date) as week").group("yearweek(educode_sales_follow_ups.bidded_date)").sum(:actual_amount)
|
@@ -2,12 +2,12 @@ module EducodeSales
|
|
2
2
|
class SaleTrend < ApplicationRecord
|
3
3
|
|
4
4
|
COLORS = [
|
5
|
-
'#44D7B6', '#4CACFF', '#F7B500', '#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF',
|
6
|
-
'#8B00FF', 'rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)',
|
5
|
+
'#44D7B6', '#7CFC00', '#4CACFF', "#110FD7", '#F7B500', '#48D1CC', '#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF',
|
6
|
+
'#8B00FF', 'rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)',
|
7
7
|
'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)', 'rgba(255, 0, 250, 1)', '#A2D9CE', '#D6EAF8', '#E8DAEF', '#FADBD8',
|
8
8
|
'#F5B7B1', '#E6B0AA', '#FCF3CF', '#FDEBD0', '#F6DDCC', '#D98880', '#EC7063', '#A569BD', '#85C1E9', '#45B39D', '#F7DC6F',
|
9
9
|
'#DC7633', '#839192', '#6C3483', '#B03A2E', '#7B7D7D', '#4D5656', '#28B463', '#0E6655', '#F39C12', '#0E6251', '#1B4F72',
|
10
|
-
'#FAE5D3', '#82E0AA', '#FF00FF', '#663399', '#4B0082', '#7CFC00'
|
10
|
+
'#FAE5D3', '#82E0AA', '#FF00FF', '#663399', '#4B0082', '#7CFC00'
|
11
11
|
]
|
12
12
|
end
|
13
13
|
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
module EducodeSales
|
2
|
+
class ReturnForecastService
|
3
|
+
attr_reader :user, :params, :request
|
4
|
+
# ReturnForecastService.new(current_user, request, params).call
|
5
|
+
def initialize(user, request, params)
|
6
|
+
@user = user
|
7
|
+
@request = request
|
8
|
+
@params = params
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
end
|
13
|
+
|
14
|
+
def return_forecast_quarter(labels, selects, staff_id = nil)
|
15
|
+
plan_get = plan_get(staff_id, labels, "quarter")
|
16
|
+
actual_get = actual_get(staff_id, labels, "quarter")
|
17
|
+
datasets = selects.map do |select|
|
18
|
+
if select == "计划投标额"
|
19
|
+
{
|
20
|
+
label: select,
|
21
|
+
data: labels.map do |d|
|
22
|
+
quarter = d.split("-").last.to_i
|
23
|
+
year = d.split("-").first.to_i
|
24
|
+
plan_get[[year, quarter]]&.pluck(:budget_amount)&.reject(&:blank?)&.sum.to_f
|
25
|
+
end,
|
26
|
+
backgroundColor: [
|
27
|
+
'rgba(255, 99, 132, 0.2)',
|
28
|
+
],
|
29
|
+
borderColor: [
|
30
|
+
'rgba(255,99,132,1)',
|
31
|
+
],
|
32
|
+
borderWidth: 1
|
33
|
+
}
|
34
|
+
else
|
35
|
+
{
|
36
|
+
label: select,
|
37
|
+
data: labels.map do |d|
|
38
|
+
quarter = d.split("-").last.to_i
|
39
|
+
year = d.split("-").first.to_i
|
40
|
+
actual_get[[year, quarter]]&.pluck(:actual_amount)&.reject(&:blank?)&.sum.to_f
|
41
|
+
end,
|
42
|
+
backgroundColor: [
|
43
|
+
'rgba(54, 162, 235, 0.2)',
|
44
|
+
],
|
45
|
+
borderColor: [
|
46
|
+
'rgba(54, 162, 235, 1)',
|
47
|
+
],
|
48
|
+
borderWidth: 1
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
{
|
54
|
+
labels: labels,
|
55
|
+
datasets: datasets
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def return_forecast_week(labels, selects, staff_id = nil)
|
60
|
+
|
61
|
+
plan_get = plan_get(staff_id, labels, "week")
|
62
|
+
actual_get = actual_get(staff_id, labels, "week")
|
63
|
+
datasets = selects.map do |select|
|
64
|
+
if select == "计划投标额"
|
65
|
+
{
|
66
|
+
label: select,
|
67
|
+
data: labels.map do |d|
|
68
|
+
week = d.split("-").last
|
69
|
+
year = d.split("-").first
|
70
|
+
yearweek = "#{year}#{week}".to_i
|
71
|
+
plan_get[yearweek].to_f
|
72
|
+
end,
|
73
|
+
backgroundColor: [
|
74
|
+
'rgba(255, 99, 132, 0.2)',
|
75
|
+
],
|
76
|
+
borderColor: [
|
77
|
+
'rgba(255,99,132,1)',
|
78
|
+
],
|
79
|
+
borderWidth: 1
|
80
|
+
}
|
81
|
+
else
|
82
|
+
{
|
83
|
+
label: select,
|
84
|
+
data: labels.map do |d|
|
85
|
+
week = d.split("-").last
|
86
|
+
year = d.split("-").first
|
87
|
+
yearweek = "#{year}#{week}".to_i
|
88
|
+
actual_get[yearweek].to_f
|
89
|
+
end,
|
90
|
+
backgroundColor: [
|
91
|
+
'rgba(54, 162, 235, 0.2)',
|
92
|
+
],
|
93
|
+
borderColor: [
|
94
|
+
'rgba(54, 162, 235, 1)',
|
95
|
+
],
|
96
|
+
borderWidth: 1
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
{
|
102
|
+
labels: labels,
|
103
|
+
datasets: datasets
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def return_forecast_month(labels, selects, staff_id = nil)
|
108
|
+
plan_get = plan_get(staff_id, labels, "month")
|
109
|
+
actual_get = actual_get(staff_id, labels, "month")
|
110
|
+
datasets = selects.map do |select|
|
111
|
+
if select == "计划投标额"
|
112
|
+
{
|
113
|
+
label: select,
|
114
|
+
data: labels.map do |d|
|
115
|
+
month = d.split("-").last.to_i
|
116
|
+
year = d.split("-").first.to_i
|
117
|
+
plan_get[[year, month]]&.pluck(:budget_amount)&.reject(&:blank?)&.sum.to_f
|
118
|
+
end,
|
119
|
+
backgroundColor: [
|
120
|
+
'rgba(255, 99, 132, 0.2)',
|
121
|
+
],
|
122
|
+
borderColor: [
|
123
|
+
'rgba(255,99,132,1)',
|
124
|
+
],
|
125
|
+
borderWidth: 1
|
126
|
+
}
|
127
|
+
else
|
128
|
+
{
|
129
|
+
label: select,
|
130
|
+
data: labels.map do |d|
|
131
|
+
month = d.split("-").last.to_i
|
132
|
+
year = d.split("-").first.to_i
|
133
|
+
actual_get[[year, month]]&.pluck(:actual_amount)&.reject(&:blank?)&.sum.to_f
|
134
|
+
end,
|
135
|
+
backgroundColor: [
|
136
|
+
'rgba(54, 162, 235, 0.2)',
|
137
|
+
],
|
138
|
+
borderColor: [
|
139
|
+
'rgba(54, 162, 235, 1)',
|
140
|
+
],
|
141
|
+
borderWidth: 1
|
142
|
+
}
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
{
|
147
|
+
labels: labels,
|
148
|
+
datasets: datasets
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
def return_forecast_year(labels, selects, staff_id = nil)
|
153
|
+
plan_get = plan_get(staff_id, labels, "year")
|
154
|
+
actual_get = actual_get(staff_id, labels, "year")
|
155
|
+
datasets = selects.map do |select|
|
156
|
+
if select == "计划投标额"
|
157
|
+
{
|
158
|
+
label: select,
|
159
|
+
data: labels.map { |d| plan_get[d.to_i] || 0 },
|
160
|
+
backgroundColor: [
|
161
|
+
'rgba(255, 99, 132, 0.2)',
|
162
|
+
],
|
163
|
+
borderColor: [
|
164
|
+
'rgba(255,99,132,1)',
|
165
|
+
],
|
166
|
+
borderWidth: 1
|
167
|
+
}
|
168
|
+
else
|
169
|
+
{
|
170
|
+
label: select,
|
171
|
+
data: labels.map { |d| actual_get[d.to_i] || 0 },
|
172
|
+
backgroundColor: [
|
173
|
+
'rgba(54, 162, 235, 0.2)',
|
174
|
+
],
|
175
|
+
borderColor: [
|
176
|
+
'rgba(54, 162, 235, 1)',
|
177
|
+
],
|
178
|
+
borderWidth: 1
|
179
|
+
}
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
{
|
184
|
+
labels: labels,
|
185
|
+
datasets: datasets
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
def plan_get(staff_id, time_range, type)
|
192
|
+
# budget_amount 预算额
|
193
|
+
staff_id = staff_id.present? ? Array(staff_id) : nil
|
194
|
+
data = Business.joins(:last_follow_up)
|
195
|
+
.where("educode_sales_follow_ups.invitation_at >= ? and educode_sales_follow_ups.invitation_at <= ? #{staff_id.present? ? "AND educode_sales_follow_ups.staff_id in (#{staff_id.join(",")})" : ""}", time_range.first, time_range.last)
|
196
|
+
case type
|
197
|
+
when "week"
|
198
|
+
data.select("educode_sales_follow_ups.*, yearweek(educode_sales_follow_ups.invitation_at) as week").group("yearweek(educode_sales_follow_ups.invitation_at)").sum(:budget_amount)
|
199
|
+
when "quarter"
|
200
|
+
data = data.select("educode_sales_follow_ups.*, quarter(educode_sales_follow_ups.invitation_at) as quarter, year(educode_sales_follow_ups.invitation_at) as year")
|
201
|
+
data.group_by { |d| [d.year, d.quarter] }
|
202
|
+
when "month"
|
203
|
+
data = data.select("educode_sales_follow_ups.*, year(educode_sales_follow_ups.invitation_at) as year, month(educode_sales_follow_ups.invitation_at) as month")
|
204
|
+
data.group_by { |d| [d.year, d.month] }
|
205
|
+
when "year"
|
206
|
+
data.select("educode_sales_follow_ups.*, year(educode_sales_follow_ups.invitation_at) as year").group("year(educode_sales_follow_ups.invitation_at)").sum(:budget_amount)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def actual_get(staff_id, time_range, type)
|
211
|
+
# actual_amount 合同额
|
212
|
+
staff_id = staff_id.present? ? Array(staff_id) : nil
|
213
|
+
data = Business.joins(:last_follow_up)
|
214
|
+
.where("educode_sales_follow_ups.bidded_date >= ? and educode_sales_follow_ups.bidded_date <= ? #{staff_id.present? ? "AND educode_sales_follow_ups.staff_id in (#{staff_id.join(",")})" : ""}", time_range.first, time_range.last)
|
215
|
+
case type
|
216
|
+
when "week"
|
217
|
+
data.select("educode_sales_follow_ups.*, yearweek(educode_sales_follow_ups.bidded_date) as week").group("yearweek(educode_sales_follow_ups.bidded_date)").sum(:actual_amount)
|
218
|
+
when "quarter"
|
219
|
+
data = data.select("educode_sales_follow_ups.*, quarter(educode_sales_follow_ups.bidded_date) as quarter, year(educode_sales_follow_ups.bidded_date) as year")
|
220
|
+
data.group_by { |d| [d.year, d.quarter] }
|
221
|
+
when "month"
|
222
|
+
data = data.select("educode_sales_follow_ups.*, year(educode_sales_follow_ups.bidded_date) as year, month(educode_sales_follow_ups.bidded_date) as month")
|
223
|
+
data.group_by { |d| [d.year, d.month] }
|
224
|
+
when "year"
|
225
|
+
data.select("educode_sales_follow_ups.*, year(educode_sales_follow_ups.bidded_date) as year").group("year(educode_sales_follow_ups.bidded_date)").sum(:actual_amount)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
end
|
@@ -102,7 +102,7 @@
|
|
102
102
|
<hr>
|
103
103
|
<div class="layui-inline">
|
104
104
|
<label class="layui-form-label">计划签单时间:</label>
|
105
|
-
<div class="layui-input-inline" style="line-height: 38px;">
|
105
|
+
<div class="layui-input-inline" style="line-height: 38px;width: 100px;">
|
106
106
|
<input type="text" class="layui-input date" name="plan_signed_date" autocomplete="off" value="<%= @last_follow_up&.plan_signed_date %>">
|
107
107
|
</div>
|
108
108
|
</div>
|
@@ -115,10 +115,16 @@
|
|
115
115
|
</div>
|
116
116
|
<div class="layui-inline">
|
117
117
|
<label class="layui-form-label" style="width:100px;">实际签单时间:</label>
|
118
|
-
<div class="layui-input-inline" style="line-height: 38px;">
|
118
|
+
<div class="layui-input-inline" style="line-height: 38px;width: 100px;">
|
119
119
|
<input type="text" class="layui-input date" name="signed_date" autocomplete="off" value="<%= @last_follow_up&.signed_date %>">
|
120
120
|
</div>
|
121
121
|
</div>
|
122
|
+
<div class="layui-inline">
|
123
|
+
<label class="layui-form-label">签约类型:</label>
|
124
|
+
<div class="layui-input-inline" style="line-height: 38px;width:110px;">
|
125
|
+
<%= select_tag "signed_clazz", options_for_select(@signed_clazz, @last_follow_up&.signed_clazz), class: 'required' %>
|
126
|
+
</div>
|
127
|
+
</div>
|
122
128
|
<div class="layui-inline">
|
123
129
|
<label class="layui-form-label" style="width:100px;">签约单位:</label>
|
124
130
|
<div class="layui-input-inline" style="line-height: 38px;">
|
@@ -611,7 +617,7 @@
|
|
611
617
|
initValue: gon.value, // 渲染初始化默认值
|
612
618
|
hasSelectIcon: false,
|
613
619
|
placeholder: '请输入头歌平台上单位部门', // 渲染的inputplaceholder值
|
614
|
-
data:
|
620
|
+
data: gon.department,
|
615
621
|
remoteSearch: true, // 是否启用远程搜索 默认是false,和远程搜索回调保存同步
|
616
622
|
remoteMethod: function (value, cb) { // 远程搜索的回调函数
|
617
623
|
if (!value) {
|
File without changes
|
@@ -71,6 +71,8 @@
|
|
71
71
|
<dd><a href="/missions/contracts/product_list" class="<%= current?('layui-this', product_list_contracts_path) %>"><i style="padding-right: 35px"></i><%= image_tag "educode_sales/4.商机管理.png",size:"15"%><i style="font-size: 25px; padding-right: 15px"></i>产品目录</a>
|
72
72
|
</dd>
|
73
73
|
<% end %>
|
74
|
+
<dd><a href="/missions/sales_details" class="<%= current?('layui-this', sales_details_path) %>"><i style="padding-right: 35px"></i><%= image_tag "educode_sales/4.商机管理.png",size:"15"%><i style="font-size: 25px; padding-right: 15px"></i>销售明细</a>
|
75
|
+
</dd>
|
74
76
|
<% if can? :read, EducodeSales::MoneyPlan %>
|
75
77
|
<dd><a href="/missions/money_plans" class="<%= current?('layui-this', money_plans_path) %>"><i style="padding-right: 35px"></i><%= image_tag "educode_sales/money_plans.png",size:"15"%><i style="font-size: 25px; padding-right: 15px"></i>回款计划</a>
|
76
78
|
</dd>
|
data/config/routes.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateEducodeSalesSalesDetails < ActiveRecord::Migration[5.2]
|
2
|
+
def change
|
3
|
+
create_table :educode_sales_sales_details do |t|
|
4
|
+
t.references :business
|
5
|
+
t.references :product_catalog
|
6
|
+
t.references :staff
|
7
|
+
t.float :price
|
8
|
+
t.float :amount
|
9
|
+
t.float :total_price
|
10
|
+
t.integer :custom_clazz
|
11
|
+
t.date :delivery_date
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: educode_sales
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.84
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- anke1460
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -173,6 +173,7 @@ files:
|
|
173
173
|
- app/controllers/educode_sales/sale_reports_controller.rb
|
174
174
|
- app/controllers/educode_sales/sale_trends_controller.rb
|
175
175
|
- app/controllers/educode_sales/sales_controller.rb
|
176
|
+
- app/controllers/educode_sales/sales_details_controller.rb
|
176
177
|
- app/controllers/educode_sales/sessions_controller.rb
|
177
178
|
- app/controllers/educode_sales/staffs_controller.rb
|
178
179
|
- app/controllers/educode_sales/teacher_follows_controller.rb
|
@@ -243,6 +244,7 @@ files:
|
|
243
244
|
- app/models/educode_sales/sale_plan.rb
|
244
245
|
- app/models/educode_sales/sale_report.rb
|
245
246
|
- app/models/educode_sales/sale_trend.rb
|
247
|
+
- app/models/educode_sales/sales_detail.rb
|
246
248
|
- app/models/educode_sales/staff.rb
|
247
249
|
- app/models/educode_sales/staff_permission.rb
|
248
250
|
- app/models/educode_sales/staff_school.rb
|
@@ -250,6 +252,7 @@ files:
|
|
250
252
|
- app/models/educode_sales/teacher.rb
|
251
253
|
- app/models/educode_sales/teacher_assign_follow.rb
|
252
254
|
- app/models/educode_sales/teacher_follow.rb
|
255
|
+
- app/services/return_forecast_service.rb
|
253
256
|
- app/views/educode_sales/activities/_follows.html.erb
|
254
257
|
- app/views/educode_sales/activities/_index.html.erb
|
255
258
|
- app/views/educode_sales/activities/edit.html.erb
|
@@ -535,6 +538,7 @@ files:
|
|
535
538
|
- app/views/educode_sales/sales/school.html.erb
|
536
539
|
- app/views/educode_sales/sales/trends.html.erb
|
537
540
|
- app/views/educode_sales/sales/xschool.html.erb
|
541
|
+
- app/views/educode_sales/sales_details/index.html.erb
|
538
542
|
- app/views/educode_sales/sessions/login.html.erb
|
539
543
|
- app/views/educode_sales/staffs/edit.html.erb
|
540
544
|
- app/views/educode_sales/staffs/follow_up_departments.json.jbuilder
|
@@ -666,6 +670,7 @@ files:
|
|
666
670
|
- db/migrate/20230430134710_create_educode_sales_assign_staffs.rb
|
667
671
|
- db/migrate/20230501034307_create_educode_sales_follow_up_trends.rb
|
668
672
|
- db/migrate/20230502142912_create_educode_sales_product_catalogs.rb
|
673
|
+
- db/migrate/20230510144317_create_educode_sales_sales_details.rb
|
669
674
|
- lib/educode_sales.rb
|
670
675
|
- lib/educode_sales/engine.rb
|
671
676
|
- lib/educode_sales/version.rb
|