Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5675.3 batch create IS lines from IO lines #6384

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions server/graphql/batch_mutations/src/batch_inbound_shipment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ type ServiceResult = BatchInboundShipmentResult;
name = "InsertInboundShipmentLineResponseWithId",
params(inbound_shipment_line::line::insert::InsertResponse)
))]
#[graphql(concrete(
name = "InsertInboundShipmentLineFromInternalOrderLineResponseWithId",
params(
inbound_shipment_line::line::insert_from_internal_order::InsertFromInternalOrderResponse
)
))]
#[graphql(concrete(
name = "UpdateInboundShipmentLineResponseWithId",
params(inbound_shipment_line::line::update::UpdateResponse)
Expand Down Expand Up @@ -60,6 +66,8 @@ type InsertShipmentsResponse =
Option<Vec<MutationWithId<inbound_shipment::insert::InsertResponse>>>;
type InsertShipmentLinesResponse =
Option<Vec<MutationWithId<inbound_shipment_line::line::insert::InsertResponse>>>;
type InsertFromInternalOrderLinesResponse =
Option<Vec<MutationWithId<inbound_shipment_line::line::insert_from_internal_order::InsertFromInternalOrderResponse>>>;
type UpdateShipmentLinesResponse =
Option<Vec<MutationWithId<inbound_shipment_line::line::update::UpdateResponse>>>;
type DeleteShipmentLinesResponse =
Expand All @@ -80,6 +88,7 @@ type DeleteShipmentsResponse =
pub struct BatchResponse {
insert_inbound_shipments: InsertShipmentsResponse,
insert_inbound_shipment_lines: InsertShipmentLinesResponse,
insert_from_internal_order_lines: InsertFromInternalOrderLinesResponse,
update_inbound_shipment_lines: UpdateShipmentLinesResponse,
delete_inbound_shipment_lines: DeleteShipmentLinesResponse,
insert_inbound_shipment_service_lines: InsertShipmentServiceLinesResponse,
Expand All @@ -95,6 +104,9 @@ pub struct BatchInput {
pub insert_inbound_shipments: Option<Vec<inbound_shipment::insert::InsertInput>>,
pub insert_inbound_shipment_lines:
Option<Vec<inbound_shipment_line::line::insert::InsertInput>>,
pub insert_from_internal_order_lines: Option<
Vec<inbound_shipment_line::line::insert_from_internal_order::InsertFromInternalOrderLine>,
>,
pub update_inbound_shipment_lines:
Option<Vec<inbound_shipment_line::line::update::UpdateInput>>,
pub delete_inbound_shipment_lines:
Expand Down Expand Up @@ -134,6 +146,7 @@ impl BatchInput {
let BatchInput {
insert_inbound_shipments,
insert_inbound_shipment_lines,
insert_from_internal_order_lines,
update_inbound_shipment_lines,
delete_inbound_shipment_lines,
insert_inbound_shipment_service_lines,
Expand All @@ -149,6 +162,8 @@ impl BatchInput {
.map(|inputs| inputs.into_iter().map(|input| input.to_domain()).collect()),
insert_line: insert_inbound_shipment_lines
.map(|inputs| inputs.into_iter().map(|input| input.to_domain()).collect()),
insert_from_internal_order_lines: insert_from_internal_order_lines
.map(|inputs| inputs.into_iter().map(|input| input.to_domain()).collect()),
update_line: update_inbound_shipment_lines
.map(|inputs| inputs.into_iter().map(|input| input.to_domain()).collect()),
delete_line: delete_inbound_shipment_lines
Expand All @@ -173,6 +188,7 @@ impl BatchResponse {
ServiceResult {
insert_shipment,
insert_line,
insert_from_internal_order_lines,
update_line,
delete_line,
insert_service_line,
Expand All @@ -185,6 +201,9 @@ impl BatchResponse {
let result = BatchResponse {
insert_inbound_shipments: map_insert_shipments(insert_shipment)?,
insert_inbound_shipment_lines: map_insert_lines(insert_line)?,
insert_from_internal_order_lines: map_from_internal_order_lines(
insert_from_internal_order_lines,
)?,
update_inbound_shipment_lines: map_update_lines(update_line)?,
delete_inbound_shipment_lines: map_delete_lines(delete_line)?,
insert_inbound_shipment_service_lines: map_insert_service_lines(insert_service_line)?,
Expand Down Expand Up @@ -265,6 +284,29 @@ fn map_insert_lines(responses: InsertLinesResult) -> Result<InsertShipmentLinesR
Ok(result.vec_or_none())
}

fn map_from_internal_order_lines(
responses: InsertFromInternalOrderLinesResult,
) -> Result<InsertFromInternalOrderLinesResponse> {
let mut result = Vec::new();
for response in responses {
let mapped_response =
match inbound_shipment_line::line::insert_from_internal_order::map_response(
response.result,
) {
Ok(response) => response,
Err(standard_error) => {
return Err(to_standard_error(response.input, standard_error))
}
};
result.push(MutationWithId {
id: response.input.requisition_line_id.clone(),
response: mapped_response,
});
}

Ok(result.vec_or_none())
}

fn map_update_lines(responses: UpdateLinesResult) -> Result<UpdateShipmentLinesResponse> {
let mut result = Vec::new();
for response in responses {
Expand Down Expand Up @@ -591,6 +633,7 @@ mod test {
}),
result: Err(InsertStockInLineError::InvoiceDoesNotExist {}),
}],
insert_from_internal_order_lines: vec![],
update_line: vec![InputWithResult {
input: inline_init(|input: &mut UpdateStockInLine| {
input.id = "id3".to_string()
Expand Down Expand Up @@ -644,6 +687,7 @@ mod test {
}),
result: Err(InsertStockInLineError::InvoiceDoesNotExist {}),
}],
insert_from_internal_order_lines: vec![],
update_line: vec![],
delete_line: vec![],
update_shipment: vec![],
Expand Down Expand Up @@ -710,6 +754,7 @@ mod test {
input.invoice_line_row.id = "id3".to_string()
})),
}],
insert_from_internal_order_lines: vec![],
delete_line: vec![],
update_shipment: vec![],
delete_shipment: vec![],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use async_graphql::*;
use graphql_core::{
standard_graphql_error::{validate_auth, StandardGraphqlError},
ContextExt,
};
use graphql_types::types::InvoiceLineNode;
use repository::InvoiceLine;
use service::auth::{Resource, ResourceAccessRequest};
use service::invoice_line::inbound_shipment_from_internal_order_lines::{
InsertFromInternalOrderLine as ServiceInput, InsertFromInternalOrderLineError as ServiceError,
};

#[derive(InputObject)]
#[graphql(name = "InsertInboundShipmentLineFromInternalOrderLineInput")]
pub struct InsertFromInternalOrderLine {
pub invoice_id: String,
pub requisition_line_id: String,
}

#[derive(Union)]
pub enum InsertFromInternalOrderResponse {
Response(InvoiceLineNode),
}

pub fn insert_from_internal_order_line(
ctx: &Context<'_>,
store_id: &str,
input: InsertFromInternalOrderLine,
) -> Result<InsertFromInternalOrderResponse> {
let user = validate_auth(
ctx,
&ResourceAccessRequest {
resource: Resource::MutateInboundShipment,
store_id: Some(store_id.to_string()),
},
)?;

let service_provider = ctx.service_provider();
let service_context = service_provider.context(store_id.to_string(), user.user_id)?;
let result = service_provider
.invoice_line_service
.insert_from_internal_order_line(&service_context, input.to_domain());

map_response(result)
}

pub fn map_response(
from: Result<InvoiceLine, ServiceError>,
) -> Result<InsertFromInternalOrderResponse> {
let result = match from {
Ok(invoice) => {
InsertFromInternalOrderResponse::Response(InvoiceLineNode::from_domain(invoice))
}
Err(error) => return map_error(error),
};

Ok(result)
}

fn map_error(error: ServiceError) -> Result<InsertFromInternalOrderResponse> {
use ServiceError::*;
let formatted_error = format!("{:#?}", error);

let graphql_error = match error {
InvoiceDoesNotExist
| NotThisStoreInvoice
| CannotEditFinalised
| NotAnInboundShipment
| RequisitionLineDoesNotExist
| RequisitionNotLinkedToInvoice
| CannotAddLineFromInternalOrder
| ItemDoesNotExist => StandardGraphqlError::BadUserInput(formatted_error),
NewlyCreatedLineDoesNotExist | DatabaseError(_) => {
StandardGraphqlError::InternalError(formatted_error)
}
};

Err(graphql_error.extend())
}

impl InsertFromInternalOrderLine {
pub fn to_domain(self) -> ServiceInput {
let InsertFromInternalOrderLine {
invoice_id,
requisition_line_id,
} = self;

ServiceInput {
invoice_id,
requisition_line_id,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use async_graphql::*;

pub mod delete;
pub mod insert;
pub mod insert_from_internal_order;
pub mod update;

pub struct BatchIsReserved;
Expand Down
22 changes: 22 additions & 0 deletions server/service/src/invoice/inbound_shipment/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use repository::{Invoice, InvoiceLine, RepositoryError};

use crate::{
invoice_line::{
inbound_shipment_from_internal_order_lines::{
insert_from_internal_order_line, InsertFromInternalOrderLine,
InsertFromInternalOrderLineError,
},
inbound_shipment_service_line::{
delete_inbound_shipment_service_line, insert_inbound_shipment_service_line,
update_inbound_shipment_service_line, DeleteInboundShipmentServiceLineError,
Expand All @@ -28,6 +32,7 @@ use super::{
pub struct BatchInboundShipment {
pub insert_shipment: Option<Vec<InsertInboundShipment>>,
pub insert_line: Option<Vec<InsertStockInLine>>,
pub insert_from_internal_order_lines: Option<Vec<InsertFromInternalOrderLine>>,
pub update_line: Option<Vec<UpdateStockInLine>>,
pub delete_line: Option<Vec<DeleteStockInLine>>,
pub insert_service_line: Option<Vec<InsertInboundShipmentServiceLine>>,
Expand All @@ -42,6 +47,12 @@ pub type InsertShipmentsResult =
Vec<InputWithResult<InsertInboundShipment, Result<Invoice, InsertInboundShipmentError>>>;
pub type InsertLinesResult =
Vec<InputWithResult<InsertStockInLine, Result<InvoiceLine, InsertStockInLineError>>>;
pub type InsertFromInternalOrderLinesResult = Vec<
InputWithResult<
InsertFromInternalOrderLine,
Result<InvoiceLine, InsertFromInternalOrderLineError>,
>,
>;
pub type UpdateLinesResult =
Vec<InputWithResult<UpdateStockInLine, Result<InvoiceLine, UpdateStockInLineError>>>;
pub type DeleteLinesResult =
Expand Down Expand Up @@ -69,6 +80,7 @@ pub type DeleteShipmentsResult =
pub struct BatchInboundShipmentResult {
pub insert_shipment: InsertShipmentsResult,
pub insert_line: InsertLinesResult,
pub insert_from_internal_order_lines: InsertFromInternalOrderLinesResult,
pub update_line: UpdateLinesResult,
pub delete_line: DeleteLinesResult,
pub insert_service_line: InsertServiceLinesResult,
Expand Down Expand Up @@ -99,6 +111,15 @@ pub fn batch_inbound_shipment(
return Err(WithDBError::err(results));
}

let (has_errors, result) = mutations_processor.do_mutations_with_user_id(
input.insert_from_internal_order_lines,
insert_from_internal_order_line,
);
results.insert_from_internal_order_lines = result;
if has_errors && !continue_on_error {
return Err(WithDBError::err(results));
}

// Normal Line

let (has_errors, result) = mutations_processor
Expand Down Expand Up @@ -230,6 +251,7 @@ mod test {
input.number_of_packs = 1.0;
input.r#type = StockInType::InboundShipment;
})]),
insert_from_internal_order_lines: None,
update_line: None,
delete_line: None,
update_shipment: None,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use crate::{
invoice::common::generate_invoice_user_id_update,
invoice_line::stock_in_line::{generate_batch, StockLineInput},
};
use repository::{
InvoiceLineRow, InvoiceLineType, InvoiceRow, ItemRow, RepositoryError, RequisitionLineRow,
StockLineRow, StorageConnection,
};
use util::uuid::uuid;

pub struct GenerateResult {
pub invoice: Option<InvoiceRow>,
pub invoice_line: InvoiceLineRow,
pub stock_line: StockLineRow,
}

pub fn generate(
connection: &StorageConnection,
user_id: &str,
item_row: ItemRow,
existing_invoice_row: InvoiceRow,
requisition_row: RequisitionLineRow,
) -> Result<GenerateResult, RepositoryError> {
let mut invoice_line = generate_line(requisition_row, item_row, existing_invoice_row.clone());

let stock_line = generate_batch(
connection,
invoice_line.clone(),
StockLineInput {
stock_line_id: None,
store_id: existing_invoice_row.store_id.clone(),
supplier_link_id: existing_invoice_row.name_link_id.clone(),
on_hold: false,
barcode_id: None,
overwrite_stock_levels: true,
},
)?;
// If a new stock line has been created, update the stock_line_id on the invoice line
invoice_line.stock_line_id = Some(stock_line.id.clone());

Ok(GenerateResult {
invoice: generate_invoice_user_id_update(user_id, existing_invoice_row),
invoice_line,
stock_line,
})
}

fn generate_line(
RequisitionLineRow {
requested_quantity,
comment: note,
..
}: RequisitionLineRow,
ItemRow {
id: item_id,
name: item_name,
code: item_code,
..
}: ItemRow,
InvoiceRow {
id: invoice_id,
tax_percentage,
..
}: InvoiceRow,
) -> InvoiceLineRow {
InvoiceLineRow {
id: uuid(),
invoice_id,
item_link_id: item_id,
pack_size: 1.0,
note,
r#type: InvoiceLineType::StockIn,
number_of_packs: requested_quantity,
item_name,
item_code,
// Deafults
stock_line_id: None,
total_before_tax: 0.0,
total_after_tax: 0.0,
tax_percentage,
sell_price_per_pack: 0.0,
cost_price_per_pack: 0.0,
batch: None,
expiry_date: None,
item_variant_id: None,
location_id: None,
inventory_adjustment_reason_id: None,
return_reason_id: None,
foreign_currency_price_before_tax: None,
}
}
Loading
Loading