Skip to content

Commit 2a4d1a6

Browse files
Merge pull request #6859 from msupply-foundation/6651-approved-quantity-in-requisition
6651 display approved quantity in requisition, update chart calculations
2 parents 5417317 + 8382ed2 commit 2a4d1a6

File tree

4 files changed

+194
-14
lines changed

4 files changed

+194
-14
lines changed

client/packages/requisitions/src/ResponseRequisition/DetailView/ResponseLineEdit/ResponseEditPage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
BasicSpinner,
44
DetailContainer,
55
NothingHere,
6+
RequisitionNodeApprovalStatus,
67
RequisitionNodeStatus,
78
RouteBuilder,
89
useBreadcrumbs,
@@ -90,6 +91,10 @@ const ResponseLineEditPageInner = ({
9091
Right={
9192
<ResponseLineEdit
9293
hasLinkedRequisition={!!requisition.linkedRequisition}
94+
hasApproval={
95+
requisition.approvalStatus ===
96+
RequisitionNodeApprovalStatus.Approved
97+
}
9398
draft={draft}
9499
update={update}
95100
save={save}

client/packages/requisitions/src/ResponseRequisition/DetailView/ResponseLineEdit/ResponseLineEdit.tsx

+17
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const LABEL_WIDTH = '150px';
3333
interface ResponseLineEditProps {
3434
item?: ItemRowFragment | null;
3535
hasLinkedRequisition?: boolean | undefined;
36+
hasApproval?: boolean | undefined;
3637
draft?: DraftResponseLine | null;
3738
update: (patch: Partial<DraftResponseLine>) => void;
3839
save?: () => void;
@@ -50,6 +51,7 @@ interface ResponseLineEditProps {
5051

5152
export const ResponseLineEdit = ({
5253
hasLinkedRequisition,
54+
hasApproval,
5355
draft,
5456
update,
5557
save,
@@ -353,6 +355,21 @@ export const ResponseLineEdit = ({
353355
)}
354356
</Box>
355357
</Box>
358+
{hasApproval && (
359+
<Box>
360+
<InputWithLabelRow
361+
Input={
362+
<NumericTextInput
363+
width={INPUT_WIDTH}
364+
value={draft?.approvedQuantity}
365+
/>
366+
}
367+
labelWidth={LABEL_WIDTH}
368+
label={t('label.approved-quantity')}
369+
sx={{ marginBottom: 1 }}
370+
/>
371+
</Box>
372+
)}
356373
{isProgram && store?.preferences.extraFieldsInRequisition && (
357374
<InputWithLabelRow
358375
Input={

server/service/src/requisition_line/response_line_stats/mod.rs

+123-5
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ mod test {
7474
mock_store_b, MockData, MockDataInserts,
7575
},
7676
test_db::{setup_all, setup_all_with_data},
77-
RequisitionLineRow, RequisitionRow, RequisitionStatus,
77+
ApprovalStatusType, RequisitionLineRow, RequisitionRow, RequisitionStatus,
78+
StorePreferenceRow, StorePreferenceRowRepository,
7879
};
7980
use util::inline_init;
8081

@@ -128,6 +129,7 @@ mod test {
128129
r.name_link_id = "name_a".to_string();
129130
r.r#type = RequisitionType::Response;
130131
r.status = RequisitionStatus::New;
132+
r.approval_status = Some(ApprovalStatusType::Approved);
131133
r.created_datetime = Utc::now().naive_utc();
132134
r.max_months_of_stock = 3.0;
133135
})
@@ -139,6 +141,7 @@ mod test {
139141
r.requisition_id = "requisition_one".to_string();
140142
r.item_link_id = "item_d".to_string();
141143
r.requested_quantity = 20.0;
144+
r.approved_quantity = 12.0;
142145
})
143146
}
144147

@@ -150,7 +153,7 @@ mod test {
150153
r.requested_quantity = 15.0;
151154
r.available_stock_on_hand = 10.0;
152155
r.average_monthly_consumption = 50.0;
153-
r.suggested_quantity = 240.0;
156+
r.approved_quantity = 12.0;
154157
})
155158
}
156159

@@ -162,6 +165,7 @@ mod test {
162165
r.name_link_id = "name_b".to_string();
163166
r.r#type = RequisitionType::Response;
164167
r.status = RequisitionStatus::New;
168+
r.approval_status = Some(ApprovalStatusType::Approved);
165169
r.created_datetime = Utc::now().naive_utc();
166170
r.max_months_of_stock = 6.0;
167171
})
@@ -173,6 +177,76 @@ mod test {
173177
r.requisition_id = "requisition_two".to_string();
174178
r.item_link_id = "item_e".to_string();
175179
r.requested_quantity = 20.0;
180+
r.approved_quantity = 10.0;
181+
})
182+
}
183+
184+
fn requisition_three() -> RequisitionRow {
185+
inline_init(|r: &mut RequisitionRow| {
186+
r.id = "requisition_three".to_string();
187+
r.requisition_number = 4;
188+
r.store_id = mock_store_a().id;
189+
r.name_link_id = "name_b".to_string();
190+
r.r#type = RequisitionType::Response;
191+
r.status = RequisitionStatus::New;
192+
r.approval_status = Some(ApprovalStatusType::Approved);
193+
r.created_datetime = Utc::now().naive_utc();
194+
r.max_months_of_stock = 6.0;
195+
})
196+
}
197+
198+
fn requisition_line_three_a() -> RequisitionLineRow {
199+
inline_init(|r: &mut RequisitionLineRow| {
200+
r.id = "requisition_line_three_a".to_string();
201+
r.requisition_id = "requisition_three".to_string();
202+
r.item_link_id = "item_e".to_string();
203+
r.requested_quantity = 25.0;
204+
r.approved_quantity = 18.0;
205+
})
206+
}
207+
208+
fn requisition_four() -> RequisitionRow {
209+
inline_init(|r: &mut RequisitionRow| {
210+
r.id = "requisition_four".to_string();
211+
r.requisition_number = 5;
212+
r.store_id = mock_store_a().id;
213+
r.name_link_id = "name_b".to_string();
214+
r.r#type = RequisitionType::Response;
215+
r.status = RequisitionStatus::New;
216+
r.approval_status = Some(ApprovalStatusType::Denied);
217+
r.created_datetime = Utc::now().naive_utc();
218+
r.max_months_of_stock = 6.0;
219+
})
220+
}
221+
222+
fn requisition_line_four_a() -> RequisitionLineRow {
223+
inline_init(|r: &mut RequisitionLineRow| {
224+
r.id = "requisition_line_four_a".to_string();
225+
r.requisition_id = "requisition_four".to_string();
226+
r.item_link_id = "item_e".to_string();
227+
r.requested_quantity = 10.0;
228+
})
229+
}
230+
231+
fn requisition_five() -> RequisitionRow {
232+
inline_init(|r: &mut RequisitionRow| {
233+
r.id = "requisition_five".to_string();
234+
r.requisition_number = 5;
235+
r.store_id = mock_store_a().id;
236+
r.name_link_id = "name_b".to_string();
237+
r.r#type = RequisitionType::Response;
238+
r.status = RequisitionStatus::New;
239+
r.created_datetime = Utc::now().naive_utc();
240+
r.max_months_of_stock = 6.0;
241+
})
242+
}
243+
244+
fn requisition_line_five_a() -> RequisitionLineRow {
245+
inline_init(|r: &mut RequisitionLineRow| {
246+
r.id = "requisition_line_five_a".to_string();
247+
r.requisition_id = "requisition_five".to_string();
248+
r.item_link_id = "item_e".to_string();
249+
r.requested_quantity = 6.0;
176250
})
177251
}
178252

@@ -229,13 +303,19 @@ mod test {
229303
r.requisitions = vec![
230304
requisition_one(),
231305
requisition_two(),
306+
requisition_three(),
307+
requisition_four(),
308+
requisition_five(),
232309
request_requisition_a(),
233310
request_requisition_b(),
234311
];
235312
r.requisition_lines = vec![
236313
requisition_line_one_a(),
237314
requisition_line_one_b(),
238315
requisition_line_two_a(),
316+
requisition_line_three_a(),
317+
requisition_line_four_a(),
318+
requisition_line_five_a(),
239319
request_requisition_a_line_a(),
240320
request_requisition_b_line_a(),
241321
]
@@ -249,16 +329,54 @@ mod test {
249329
.unwrap();
250330
let service = service_provider.requisition_line_service;
251331

252-
let stats = service
332+
let stats_result = service
253333
.get_response_requisition_line_stats(&context, &requisition_line_one_b().id)
254334
.unwrap();
335+
336+
// Test when response requisition authorisation is not required
255337
let response_requisition_stats = ResponseRequisitionStats {
256338
response_store_stats: ResponseStoreStats {
257339
stock_on_hand: 0.0,
258340
stock_on_order: request_requisition_a_line_a().requested_quantity,
259341
incoming_stock: 0,
260342
requested_quantity: requisition_line_one_b().requested_quantity,
261-
other_requested_quantity: requisition_line_one_a().requested_quantity,
343+
other_requested_quantity: requisition_line_two_a().requested_quantity
344+
+ requisition_line_three_a().requested_quantity
345+
+ requisition_line_four_a().requested_quantity
346+
+ requisition_line_five_a().requested_quantity,
347+
},
348+
request_store_stats: RequestStoreStats {
349+
stock_on_hand: requisition_line_one_b().available_stock_on_hand,
350+
amc: requisition_line_one_b().average_monthly_consumption,
351+
max_months_of_stock: requisition_one().max_months_of_stock,
352+
suggested_quantity: requisition_line_one_b().suggested_quantity,
353+
},
354+
};
355+
356+
assert_eq!(stats_result, response_requisition_stats);
357+
358+
// Test Authorisation required on response requisition
359+
StorePreferenceRowRepository::new(&context.connection)
360+
.upsert_one(&StorePreferenceRow {
361+
id: mock_store_a().id,
362+
response_requisition_requires_authorisation: true,
363+
..Default::default()
364+
})
365+
.unwrap();
366+
367+
let stats_result = service
368+
.get_response_requisition_line_stats(&context, &requisition_line_one_b().id)
369+
.unwrap();
370+
371+
let requisition_stats = ResponseRequisitionStats {
372+
response_store_stats: ResponseStoreStats {
373+
stock_on_hand: 0.0,
374+
stock_on_order: request_requisition_a_line_a().requested_quantity,
375+
incoming_stock: 0,
376+
requested_quantity: requisition_line_one_b().approved_quantity,
377+
other_requested_quantity: requisition_line_two_a().approved_quantity
378+
+ requisition_line_three_a().approved_quantity
379+
+ requisition_line_five_a().requested_quantity,
262380
},
263381
request_store_stats: RequestStoreStats {
264382
stock_on_hand: requisition_line_one_b().available_stock_on_hand,
@@ -268,6 +386,6 @@ mod test {
268386
},
269387
};
270388

271-
assert_eq!(stats, response_requisition_stats);
389+
assert_eq!(stats_result, requisition_stats);
272390
}
273391
}

server/service/src/requisition_line/response_line_stats/response_line_stats.rs

+49-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use repository::{
2-
EqualFilter, InvoiceLineFilter, InvoiceLineRepository, InvoiceLineType, InvoiceStatus,
3-
InvoiceType, RepositoryError, RequisitionLine, RequisitionLineFilter,
2+
ApprovalStatusType, EqualFilter, InvoiceLineFilter, InvoiceLineRepository, InvoiceLineType,
3+
InvoiceStatus, InvoiceType, RepositoryError, RequisitionLine, RequisitionLineFilter,
44
RequisitionLineRepository, RequisitionStatus, RequisitionType, StockLineFilter,
55
StockLineRepository, StorageConnection,
66
};
77

8+
use crate::store_preference::get_store_preferences;
9+
810
#[derive(Clone, Debug, PartialEq, Default)]
911
pub struct RequestStoreStats {
1012
pub stock_on_hand: f64,
@@ -89,18 +91,56 @@ pub fn response_store_stats(
8991
.status(RequisitionStatus::Finalised.not_equal_to()),
9092
)?;
9193

92-
let other_requested_quantity = (response_requisition_lines
93-
.iter()
94-
.fold(0.0, |sum, requisition_line| {
95-
sum + requisition_line.requisition_line_row.requested_quantity
96-
}))
97-
- requisition_line.requisition_line_row.requested_quantity;
94+
let prefs = get_store_preferences(connection, &store_id)?;
95+
96+
// For current line check prefs, then calculate the quantity based on approved status
97+
let calculate_line_quantity = |line: &RequisitionLine| -> f64 {
98+
if !prefs.response_requisition_requires_authorisation {
99+
line.requisition_line_row.requested_quantity
100+
} else {
101+
match line.requisition_row.approval_status {
102+
Some(ApprovalStatusType::Approved)
103+
| Some(ApprovalStatusType::ApprovedByAnother)
104+
| Some(ApprovalStatusType::AutoApproved) => {
105+
if line.requisition_line_row.approved_quantity > 0.0 {
106+
line.requisition_line_row.approved_quantity
107+
} else {
108+
line.requisition_line_row.requested_quantity
109+
}
110+
}
111+
Some(ApprovalStatusType::Denied)
112+
| Some(ApprovalStatusType::DeniedByAnother)
113+
| Some(ApprovalStatusType::Pending) => 0.0,
114+
115+
Some(ApprovalStatusType::None) | None => {
116+
line.requisition_line_row.requested_quantity
117+
}
118+
}
119+
}
120+
};
121+
122+
// Sum of all lines in other requisitions
123+
let calculate_other_requested_quantity = |current_line_quantity: f64| -> f64 {
124+
let sum_of_lines = response_requisition_lines
125+
.iter()
126+
.map(|line| {
127+
let line_quantity = calculate_line_quantity(line);
128+
println!("Line quantity: {}", line_quantity);
129+
line_quantity
130+
})
131+
.sum::<f64>();
132+
133+
sum_of_lines - current_line_quantity
134+
};
135+
136+
let current_line_quantity = calculate_line_quantity(requisition_line);
137+
let other_requested_quantity = calculate_other_requested_quantity(current_line_quantity);
98138

99139
Ok(ResponseStoreStats {
100140
stock_on_hand,
101141
stock_on_order,
102142
incoming_stock,
103-
requested_quantity: requisition_line.requisition_line_row.requested_quantity,
143+
requested_quantity: current_line_quantity,
104144
other_requested_quantity,
105145
})
106146
}

0 commit comments

Comments
 (0)