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

5137.1 requisition line edit + UI updates to line columns #5254

Merged
Merged
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
13 changes: 11 additions & 2 deletions client/packages/common/src/intl/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@
"description.fc-line-total": "Foreign currency line total",
"description.fc-sell-price": "Foreign currency sell price per pack",
"description.forecast-quantity": "Forecast Quantity to Reach Target",
"description.initial-stock-on-hand": "Stock on hand on the first day of the program period",
"description.available-stock": "The customer's initial stock on hand + incoming stock +/- inventory adjustments - outgoing stock",
"description.invoice-number": "Shipment number",
"description.last-reading-datetime": "Date and time of the last reading",
"description.max-min-temperature": "Maximum or minimum temperature reading",
Expand Down Expand Up @@ -232,9 +234,9 @@
"error.failed-to-delete-item-variant": "Failed to delete item variant",
"error.failed-to-generate-excel": "Failed to generate Excel",
"error.failed-to-generate-report": "Failed to generate report",
"error.failed-to-save-item-variant": "Failed to save item variant",
"error.failed-to-save-service-charges": "Failed to save service charges",
"error.failed-to-save-vaccination": "Failed to save vaccination!",
"error.failed-to-save-item-variant": "Failed to save item variant",
"error.failed-to-save-vaccine-course": "Failed to save vaccine course",
"error.fetch-notifications": "Unable to display cold chain notifications",
"error.field-must-be-specified": "{{field}} must be specified",
Expand Down Expand Up @@ -503,6 +505,7 @@
"label.add-new-vaccine-course": "Add vaccine course",
"label.add-variant": "Add variant",
"label.additional-info": "Additional Info",
"label.additions": "Additions",
"label.address": "Address",
"label.adjust-by": "by",
"label.adjusted": "Adjusted",
Expand Down Expand Up @@ -531,6 +534,7 @@
"label.asset-properties": "Asset Properties",
"label.atc-category": "ATC category",
"label.auth-status": "Approval Status",
"label.available": "Available",
"label.available-batches": "Available batches",
"label.available-packs": "Available (packs)",
"label.available-quantity": "Available quantity: {{number}} units",
Expand Down Expand Up @@ -570,7 +574,6 @@
"label.cold-consecutive": "Cold Consecutive",
"label.cold-cumulative": "Cold Cumulative",
"label.cold-storage-location": "Cold storage location",
"label.storage-type": "Storage type",
"label.collapse": "Collapse",
"label.collapse-all": "Collapse all",
"label.color": "Colour",
Expand Down Expand Up @@ -616,6 +619,7 @@
"label.date-of-birth": "Date of Birth",
"label.date-time": "Date time",
"label.day": "day",
"label.days-out-of-stock": "Days out of stock",
"label.ddd": "Defined Daily Dose",
"label.deceased": "Deceased",
"label.decrease-qty": "Decrease quantity",
Expand Down Expand Up @@ -718,8 +722,10 @@
"label.inbound-not-delivered": "Inbound shipments not delivered",
"label.inbound-shipment": "Inbound shipment",
"label.inbound-shipment-cant-delete-reserved-line": "Batch {{batch}} (item code {{itemCode}}) is already reserved/issued",
"label.incoming": "Incoming",
"label.incoming-stock": "Stock arriving",
"label.increase-qty": "Increase quantity",
"label.initial-stock-on-hand": "Initial SOH",
"label.initialise-store-properties": "Initialise store properties for GAPS",
"label.initialised": "Initialised",
"label.installation-date": "Installation date",
Expand Down Expand Up @@ -836,6 +842,7 @@
"label.out-of-stock": "Out of stock",
"label.outbound-shipment": "Outbound shipment",
"label.outer-pack-size": "Outer pack size",
"label.outgoing": "Outgoing",
"label.pack": "Pack",
"label.pack-cost-price": "Pack cost price",
"label.pack-quantity": "Pack Qty",
Expand Down Expand Up @@ -935,6 +942,7 @@
"label.shipment-created": "Shipment created",
"label.shipments": "Shipments",
"label.shipped": "Shipped",
"label.short-expiry": "Short Expiry",
"label.showing": "Showing",
"label.site": "Site:",
"label.snapshot-num-of-packs": "Snapshot Packs",
Expand All @@ -953,6 +961,7 @@
"label.stocktake-comment": "Comment",
"label.stocktake-date": "Stocktake Date",
"label.stocktake-frequency": "Stocktake frequency",
"label.storage-type": "Storage type",
"label.store": "Store",
"label.strength": "Strength",
"label.sub-catalogue": "Sub catalogue",
Expand Down
23 changes: 21 additions & 2 deletions client/packages/common/src/types/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2934,13 +2934,21 @@ export type InsertResponseRequisitionLineErrorInterface = {
};

export type InsertResponseRequisitionLineInput = {
additionInUnits?: InputMaybe<Scalars['Float']['input']>;
averageMonthlyConsumption?: InputMaybe<Scalars['Float']['input']>;
comment?: InputMaybe<Scalars['String']['input']>;
daysOutOfStock?: InputMaybe<Scalars['Float']['input']>;
expiringUnits?: InputMaybe<Scalars['Float']['input']>;
id: Scalars['String']['input'];
incomingUnits?: InputMaybe<Scalars['Float']['input']>;
itemId: Scalars['String']['input'];
lossInUnits?: InputMaybe<Scalars['Float']['input']>;
optionId?: InputMaybe<Scalars['String']['input']>;
outgoingUnits?: InputMaybe<Scalars['Float']['input']>;
requestedQuantity?: InputMaybe<Scalars['Float']['input']>;
requisitionId: Scalars['String']['input'];
stockOnHand?: InputMaybe<Scalars['Float']['input']>;
supplyQuantity?: InputMaybe<Scalars['Float']['input']>;
theirStockOnHand?: InputMaybe<Scalars['Float']['input']>;
};

export type InsertResponseRequisitionLineResponse = InsertResponseRequisitionLineError | RequisitionLineNode;
Expand Down Expand Up @@ -6428,6 +6436,8 @@ export type RequisitionLineNode = {
alreadyIssued: Scalars['Float']['output'];
approvalComment?: Maybe<Scalars['String']['output']>;
approvedQuantity: Scalars['Float']['output'];
availableStockOnHand: Scalars['Float']['output'];
averageMonthlyConsumption: Scalars['Float']['output'];
comment?: Maybe<Scalars['String']['output']>;
daysOutOfStock: Scalars['Float']['output'];
expiringUnits: Scalars['Float']['output'];
Expand All @@ -6450,6 +6460,7 @@ export type RequisitionLineNode = {
/** OutboundShipment lines linked to requisitions line */
outboundShipmentLines: InvoiceLineConnector;
outgoingUnits: Scalars['Float']['output'];
reason?: Maybe<ReasonOptionNode>;
/**
* Quantity remaining to supply
* supplyQuantity minus all (including unallocated) linked invoice lines numberOfPacks * packSize
Expand Down Expand Up @@ -8084,11 +8095,19 @@ export type UpdateResponseRequisitionLineErrorInterface = {
};

export type UpdateResponseRequisitionLineInput = {
additionInUnits?: InputMaybe<Scalars['Float']['input']>;
averageMonthlyConsumption?: InputMaybe<Scalars['Float']['input']>;
comment?: InputMaybe<Scalars['String']['input']>;
daysOutOfStock?: InputMaybe<Scalars['Float']['input']>;
expiringUnits?: InputMaybe<Scalars['Float']['input']>;
id: Scalars['String']['input'];
incomingUnits?: InputMaybe<Scalars['Float']['input']>;
lossInUnits?: InputMaybe<Scalars['Float']['input']>;
optionId?: InputMaybe<Scalars['String']['input']>;
outgoingUnits?: InputMaybe<Scalars['Float']['input']>;
requestedQuantity?: InputMaybe<Scalars['Float']['input']>;
stockOnHand?: InputMaybe<Scalars['Float']['input']>;
supplyQuantity?: InputMaybe<Scalars['Float']['input']>;
theirStockOnHand?: InputMaybe<Scalars['Float']['input']>;
};

export type UpdateResponseRequisitionLineResponse = RequisitionLineNode | UpdateResponseRequisitionLineError;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const useResponseColumns = () => {
queryParams: { sortBy },
} = useUrlQueryParams({ initialSort: { key: 'itemName', dir: 'asc' } });
const { isRemoteAuthorisation } = useResponse.utils.isRemoteAuthorisation();
const { programName } = useResponse.document.fields(['programName']);

const columnDefinitions: ColumnDescription<ResponseLineFragment>[] = [
getCommentPopoverColumn(),
Expand Down Expand Up @@ -56,36 +57,160 @@ export const useResponseColumns = () => {
accessor: ({ rowData }) => rowData.itemStats.availableStockOnHand,
},
],
{
];

if (!programName) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Their SOH only shows up for requisitions which aren't programs

columnDefinitions.push({
key: 'customerStockOnHand',
label: 'label.customer-soh',
description: 'description.customer-soh',
width: 100,
align: ColumnAlign.Right,
getSortValue: rowData =>
rowData.linkedRequisitionLine?.itemStats?.availableStockOnHand ?? '',
Cell: PackQuantityCell,
accessor: ({ rowData }) =>
rowData.linkedRequisitionLine?.itemStats.availableStockOnHand ?? 0,
getSortValue: rowData => rowData.availableStockOnHand,
accessor: ({ rowData }) => rowData.availableStockOnHand,
});
}
columnDefinitions.push(
// TODO: Global pref to show/hide the next columns
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting a TODO reminder for global prefs

{
key: 'initialStockOnHand',
label: 'label.initial-stock-on-hand',
width: 100,
align: ColumnAlign.Right,
sortable: false,
description: 'description.initial-stock-on-hand',
Cell: PackQuantityCell,
accessor: ({ rowData }) => rowData.initialStockOnHandUnits,
},
{
key: 'incomingStock',
label: 'label.incoming',
width: 100,
align: ColumnAlign.Right,
sortable: false,
Cell: PackQuantityCell,
accessor: ({ rowData }) => rowData.incomingUnits,
},
{
key: 'outgoingUnits',
label: 'label.outgoing',
width: 100,
align: ColumnAlign.Right,
sortable: false,
Cell: PackQuantityCell,
accessor: ({ rowData }) => rowData.outgoingUnits,
},
{
key: 'losses',
label: 'label.losses',
width: 100,
align: ColumnAlign.Right,
sortable: false,
Cell: PackQuantityCell,
accessor: ({ rowData }) => rowData.lossInUnits,
},
{
key: 'additions',
label: 'label.additions',
width: 100,
align: ColumnAlign.Right,
sortable: false,
Cell: PackQuantityCell,
accessor: ({ rowData }) => rowData.additionInUnits,
},
{
key: 'availableUnits',
label: 'label.available',
width: 100,
align: ColumnAlign.Right,
sortable: false,
description: 'description.available-stock',
Cell: PackQuantityCell,
accessor: ({ rowData }) => {
const stockOnHand = rowData.initialStockOnHandUnits;

const incomingStock = rowData.incomingUnits + rowData.additionInUnits;
const outgoingStock = rowData.lossInUnits + rowData.outgoingUnits;

return stockOnHand + incomingStock - outgoingStock;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the previous commment on using itemStats, I think this will break their available units? itemStats is their available units, so doing additional incoming/outgoing arithmatic will misreport what stock they have now (or more importantly at the end of the period) right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still confused by adding the movements to the availableStockOnHand, shouldn't it be:
availableStockOnHand = intialStockOnHand + incomingStock - outgoingStock

not:

availableStockOnHand = availableStockOnHand + incomingStock - outgoingStock?

},
},
{
key: 'expiringUnits',
label: 'label.short-expiry',
width: 100,
align: ColumnAlign.Right,
sortable: false,
Cell: PackQuantityCell,
accessor: ({ rowData }) => rowData.expiringUnits,
},
{
key: 'daysOutOfStock',
label: 'label.days-out-of-stock',
width: 100,
align: ColumnAlign.Right,
sortable: false,
Cell: PackQuantityCell,
accessor: ({ rowData }) => rowData.daysOutOfStock,
},
{
key: 'amc',
label: 'label.amc',
width: 100,
align: ColumnAlign.Right,
sortable: false,
Cell: PackQuantityCell,
accessor: ({ rowData }) => rowData.averageMonthlyConsumption,
},
{
key: 'mos',
label: 'label.months-of-stock',
width: 100,
align: ColumnAlign.Right,
sortable: false,
Cell: PackQuantityCell,
accessor: ({ rowData }) => {
const stockOnHand = rowData.initialStockOnHandUnits;
const incomingStock = rowData.incomingUnits + rowData.additionInUnits;
const outgoingStock = rowData.lossInUnits + rowData.outgoingUnits;

const available = stockOnHand + incomingStock - outgoingStock;

const averageMonthlyConsumption = rowData.averageMonthlyConsumption;

return averageMonthlyConsumption !== 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice no divide by 0. Tangentially... if a site has 0 AMC then maybe their MOS should be infinity lol

? available / averageMonthlyConsumption
: 0;
},
},
{
key: 'suggestedQuantity',
label: 'label.suggested-quantity',
width: 150,
align: ColumnAlign.Right,
sortable: false,
Cell: PackQuantityCell,
accessor: ({ rowData }) => rowData.suggestedQuantity,
},
[
'requestedQuantity',
{
getSortValue: rowData => rowData.requestedQuantity,
Cell: PackQuantityCell,
accessor: ({rowData}) => rowData.requestedQuantity,
accessor: ({ rowData }) => rowData.requestedQuantity,
width: 150,
},
],
];
]
);

if (isRemoteAuthorisation) {
columnDefinitions.push({
key: 'approvedQuantity',
label: 'label.approved-quantity',
sortable: false,
Cell: PackQuantityCell,
accessor: ({rowData}) => rowData.approvedQuantity,
accessor: ({ rowData }) => rowData.approvedQuantity,
});
columnDefinitions.push({
key: 'approvalComment',
Expand All @@ -99,18 +224,26 @@ export const useResponseColumns = () => {
{
getSortValue: rowData => rowData.supplyQuantity,
Cell: PackQuantityCell,
accessor: ({rowData}) => rowData.requestedQuantity,
accessor: ({ rowData }) => rowData.requestedQuantity,
},
]);

// TODO: Global pref to show/hide column
columnDefinitions.push({
key: 'reason',
label: 'label.reason',
sortable: false,
accessor: ({ rowData }) => rowData.reason?.reason,
});

columnDefinitions.push({
label: 'label.already-issued',
description: 'description.already-issued',
key: 'alreadyIssued',
align: ColumnAlign.Right,
getSortValue: rowData => rowData.alreadyIssued,
Cell: PackQuantityCell,
accessor: ({rowData}) => rowData.alreadyIssued,
accessor: ({ rowData }) => rowData.alreadyIssued,
width: 100,
});

Expand All @@ -121,7 +254,7 @@ export const useResponseColumns = () => {
align: ColumnAlign.Right,
getSortValue: rowData => rowData.remainingQuantityToSupply,
Cell: PackQuantityCell,
accessor: ({rowData}) => rowData.remainingQuantityToSupply,
accessor: ({ rowData }) => rowData.remainingQuantityToSupply,
});
columnDefinitions.push(GenericColumnKey.Selection);

Expand Down
Loading
Loading