diff --git a/src/config-schema.ts b/src/config-schema.ts index 8d061254..125ddc1a 100644 --- a/src/config-schema.ts +++ b/src/config-schema.ts @@ -1,25 +1,18 @@ -import { Type, validator } from "@openmrs/esm-framework"; +import { Type } from "@openmrs/esm-framework"; export const configSchema = { - casualGreeting: { - _type: Type.Boolean, + printItemCost: { + type: Type.Boolean, _default: false, - _description: "Whether to use a casual greeting (or a formal one).", + _description: "Whether to print item costs on the print out", }, - whoToGreet: { - _type: Type.Array, - _default: ["World"], - _description: - "Who should be greeted. Names will be separated by a comma and space.", - _elements: { - _type: Type.String, - }, - _validators: [ - validator((v) => v.length > 0, "At least one person must be greeted."), - ], + printBalanceOnHand: { + type: Type.Boolean, + _default: false, + _description: "Whether to print balance on hand on the print out", }, }; export type Config = { - casualGreeting: boolean; - whoToGreet: Array; + printItemCost: boolean; + printBalanceOnHand: boolean; }; diff --git a/src/constants.ts b/src/constants.ts index 9df30e97..229fa211 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -106,3 +106,89 @@ export const PRINT_LOGO = export const PRINT_LOGO_TEXT = "Ministry of Health"; export const MAIN_STORE_LOCATION_TAG = "Main Store"; + +export const BASE_OPENMRS_APP_URL = "/openmrs/"; + +export const STOCKMGMT_RESOURCE_URL = "/openmrs/stockmanagement/spa.page/"; +export const STOCKMGMT_SPA_PAGE_URL = "/openmrs/stockmanagement/spa.page"; +export const URL_PRINT_LOGO = () => + PRINT_LOGO + ? `${BASE_OPENMRS_APP_URL}${PRINT_LOGO}` + : "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; +export const CLOSE_PRINT_AFTER_PRINT = true; +export const ALLOW_STOCK_ISSUE_WITHOUT_REQUISITION = false; + +export const BASE_URL_CONFIGURED = BASE_OPENMRS_APP_URL; +export const LOGIN_URL = BASE_OPENMRS_APP_URL + "login.htm"; + +export const ROUTING_BASE_URL = "/"; +export const URL_STOCK_HOME = ROUTING_BASE_URL + "home"; + +export const URL_STOCK_OPERATIONS = ROUTING_BASE_URL + "stock-operations"; +export const URL_STOCK_OPERATIONS_ROUTES = URL_STOCK_OPERATIONS + "/*"; +export const URL_STOCK_OPERATIONS_NEW = URL_STOCK_OPERATIONS + "/new/*"; +export const URL_STOCK_OPERATIONS_NEW_OPERATION = ( + name: string, + requisitionUuid?: string +): string => + `${URL_STOCK_OPERATIONS}/new/${name}${ + requisitionUuid ? `?requisition=${requisitionUuid}` : "" + }`; +export const URL_STOCK_OPERATIONS_EDIT = URL_STOCK_OPERATIONS + "/:id"; +export const URL_STOCK_OPERATIONS_REDIRECT = ( + uuid: string, + tab?: string +): string => + `${URL_STOCK_OPERATIONS}/redirect/${uuid}${tab ? `?tab=${tab}` : ""}`; +export const URL_STOCK_OPERATION = (uuid: string, tab?: string): string => + `${URL_STOCK_OPERATIONS}/${uuid}${tab ? `?tab=${tab}` : ""}`; + +export const URL_USER_ROLE_SCOPES = ROUTING_BASE_URL + "user-role-scopes"; +export const URL_USER_ROLE_SCOPES_ROUTES = URL_USER_ROLE_SCOPES + "/*"; +export const URL_USER_ROLE_SCOPES_NEW = URL_USER_ROLE_SCOPES + "/new"; +export const URL_USER_ROLE_SCOPES_EDIT = URL_USER_ROLE_SCOPES + "/:id"; +export const URL_USER_ROLE_SCOPE = (uuid: string): string => + `${URL_USER_ROLE_SCOPES}/${uuid}`; + +export const URL_STOCK_ITEMS = ROUTING_BASE_URL + "stock-items"; +export const URL_STOCK_ITEMS_ROUTES = ROUTING_BASE_URL + "stock-items/*"; +export const URL_STOCK_ITEMS_NEW = URL_STOCK_ITEMS + "/new"; +export const URL_STOCK_ITEMS_EDIT = URL_STOCK_ITEMS + "/:id"; +export const URL_STOCK_ITEM = (uuid: string, tab?: string): string => + `${URL_STOCK_ITEMS}/${uuid}${tab ? `?tab=${tab}` : ""}`; +export const URL_STOCK_ITEMS_REDIRECT = (uuid: string, tab?: string): string => + `${URL_STOCK_ITEMS}/redirect/${uuid}${tab ? `?tab=${tab}` : ""}`; +export const URL_IMPORT_ERROR_FILE = (importSessionId: string) => + `${BASE_OPENMRS_APP_URL}ws/rest/v1/stockmanagement/stockitemimport?id=${importSessionId}`; +export const URL_IMPORT_TEMPLATE_FILE = `${BASE_OPENMRS_APP_URL}moduleResources/stockmanagement/templates/Import_Stock_Items.xlsx`; + +export const URL_STOCK_SOURCES = ROUTING_BASE_URL + "stock-sources"; +export const URL_STOCK_SOURCES_ROUTES = URL_STOCK_SOURCES + "/*"; + +export const URL_SIGN_IN = ROUTING_BASE_URL + "sign-in"; +export const URL_SIGN_OUT = ROUTING_BASE_URL + "sign-out"; + +export const URL_ACCESS_DENIED = ROUTING_BASE_URL + "access-denied"; +export const URL_NOT_FOUND = "/*"; +export const URL_WILDCARD = "*"; + +export const REACT_ROUTER_PREFIX = "#"; + +export const URL_LOCATIONS = ROUTING_BASE_URL + "locations"; +export const URL_LOCATIONS_ROUTES = ROUTING_BASE_URL + "locations/*"; +export const URL_LOCATIONS_NEW = () => + `${BASE_OPENMRS_APP_URL}admin/locations/location.form`; +export const URL_LOCATIONS_EDIT = (id: number) => + `${BASE_OPENMRS_APP_URL}admin/locations/location.form?locationId=${id}`; + +export const URL_STOCK_REPORTS = ROUTING_BASE_URL + "stock-reports"; +export const URL_STOCK_REPORTS_ROUTES = ROUTING_BASE_URL + "stock-reports/*"; +export const URL_STOCK_REPORT = (uuid: string): string => + `${URL_STOCK_REPORTS}/${uuid}`; +export const URL_BATCH_JOB_ARTIFACT = ( + uuid: string, + download: boolean +): string => + `${BASE_OPENMRS_APP_URL}ws/rest/v1/stockmanagement/batchjobartifact?id=${uuid}${ + download ? "&download=1" : "" + }`; diff --git a/src/core/print/PrintStyles.ts b/src/core/print/PrintStyles.ts new file mode 100644 index 00000000..008d9b49 --- /dev/null +++ b/src/core/print/PrintStyles.ts @@ -0,0 +1,774 @@ +export const PrintCss = ` +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the main element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on h1 elements within section and + * article contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd em font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bold; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd em font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent sub and sup elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from fieldset elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * fieldset elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to inherit in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} +/*! + * Gutenberg + * + * MIT Fabien Sa + * https://github.com/BafS/Gutenberg + */ + +* { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + } + + *, + *:before, + *:after, + p:first-letter, + div:first-letter, + blockquote:first-letter, + li:first-letter, + p:first-line, + div:first-line, + blockquote:first-line, + li:first-line { + background: transparent !important; + box-shadow: none !important; + text-shadow: none !important; + } + + html { + font-size: 16px; + margin: 0; + padding: 0; + } + + body { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + background: #fff !important; + color: #000 !important; + font-family: Arial, sans-serif, "Helvetica Neue", Helvetica; + font-size: 1rem; + line-height: 1.5; + margin: 0 auto; + text-rendering: optimizeLegibility; + } + + p, + blockquote, + table, + ul, + ol, + dl { + margin-bottom: 1.5rem; + margin-top: 0; + } + + p:last-child, + ul:last-child, + ol:last-child { + margin-bottom: 0; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + color: #000; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 1.2; + margin-bottom: 0.75rem; + margin-top: 0; + } + + h1 { + font-size: 2.5rem; + } + + h2 { + font-size: 2rem; + } + + h3 { + font-size: 1.75rem; + } + + h4 { + font-size: 1.5rem; + } + + h5 { + font-size: 1.25rem; + } + + h6 { + font-size: 1rem; + } + + a, + a:visited { + color: #000; + text-decoration: underline; + word-wrap: break-word; + } + + table { + border-collapse: collapse; + } + + thead { + display: table-header-group; + } + + table, + th, + td { + border-bottom: 1px solid #000; + } + + td, + th { + padding: 0px; + page-break-inside: avoid; + } + + code, + pre, + kbd { + border: 1px solid #bbb; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-size: 85%; + } + + code, + kbd { + padding: 3px; + } + + pre { + margin-bottom: 1.5rem; + padding: 10px 12px; + } + + pre code, + pre kbd { + border: 0; + } + + ::-webkit-input-placeholder { + color: transparent; + } + + :-moz-placeholder { + color: transparent; + } + + ::-moz-placeholder { + color: transparent; + } + + :-ms-input-placeholder { + color: transparent; + } + + blockquote { + border: 0; + border-left: 5px solid #bbb; + margin-left: 1px; + padding: 12px 1.5rem; + } + + [dir='rtl'] blockquote { + border-left: 0; + border-right: 5px solid #bbb; + margin-left: 0; + margin-right: 1px; + } + + blockquote:first-child { + margin-top: 0; + } + + blockquote p:last-child, + blockquote ul:last-child, + blockquote ol:last-child { + margin-bottom: 0; + } + + blockquote footer { + display: block; + font-size: 80%; + } + + img { + border: 0; + display: block; + max-width: 100% !important; + vertical-align: middle; + } + + hr { + border: 0; + border-bottom: 2px solid #bbb; + height: 0; + margin: 2.25rem 0; + padding: 0; + } + + dt { + font-weight: bold; + } + + dd { + margin: 0; + margin-bottom: 0.75rem; + } + + abbr[title], + acronym[title] { + border: 0; + text-decoration: none; + } + + table, + blockquote, + pre, + code, + figure, + li, + hr, + ul, + ol, + a, + tr { + page-break-inside: avoid; + } + + h2, + h3, + h4, + p, + a { + orphans: 3; + widows: 3; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + page-break-after: avoid; + page-break-inside: avoid; + } + + h1+p, + h2+p, + h3+p { + page-break-before: avoid; + } + + img { + page-break-after: auto; + page-break-before: auto; + page-break-inside: avoid; + } + + pre { + white-space: pre-wrap !important; + word-wrap: break-word; + } + + body { + padding-bottom: 1cm; + padding-left: 1cm; + padding-right: 1cm; + padding-top: 0.5cm; + } + + a[href^='http']:after, + a[href^='ftp']:after { + content: " (" attr(href) ")"; + font-size: 80%; + } + + a[href$='.jpg']:after, + a[href$='.jpeg']:after, + a[href$='.gif']:after, + a[href$='.png']:after { + display: none; + } + + abbr[title]:after, + acronym[title]:after { + content: " (" attr(title) ")"; + } + + .page-break, + .break-before, + .page-break-before { + page-break-before: always; + } + + .break-after, + .page-break-after { + page-break-after: always; + } + + .avoid-break-inside { + page-break-inside: avoid; + } + + .no-print { + display: none; + } + + a.no-reformat:after { + content: ''; + } + + abbr[title].no-reformat:after, + acronym[title].no-reformat:after { + content: ''; + } + + .no-reformat abbr:after, + .no-reformat acronym:after, + .no-reformat a:after { + content: ''; + } + /*Custom CSS*/ + .right { + display: flex; + justify-content: flex-end; + margin-left: auto; + margin-right: 0; + } + .text { + margin-top:0in; + margin-right:0in; + margin-bottom:3.0pt; + margin-left:0in; + line-height:106%; + font-size:12.5pt; + } + .logo-row { + width: 99%; + } + .logo { + width: 120; + line-height:110%; + text-align: center; + margin-top: 4pt; + } + .logo img, .logo svg { + width: 52; + height: auto; + } + span.logo-text { + font-size:8.0pt; + display: block; + white-space:nowrap; + line-height: 110%; + margin-top: 4pt; + } + .heading, .heading-row { + margin-left:12.75pt; + clear: both; + } + .heading { + font-size:14.5pt; + line-height:102%; + } + .heading-row, .heading-row table { + font-size: 13.5pt; + } + .heading-row { + margin-top: 14pt; + } + .heading table, .heading-row table, .heading-row td { + border: 0; + } + .table-data { + border: 0; + border-collapse:collapse; + margin-left:6.95pt; + font-size:12.5pt; + width: 99%; + page-break-inside: auto; + } + .table-data td, .table-data th { + padding:0 5.75pt 0 5.15pt; + } + .table-data td, .table-data th { + border-top:none; + border-left:none; + border-right:solid black 1.0pt; + vertical-align: middle; + height: 30pt; + } + .table-data th { + border-bottom:double black 1.5pt; + } + .table-data td { + border-bottom:solid black 1.0pt; + } + .table-data th:first-child, .table-data td:first-child { + border-left:solid black 1.0pt; + } + .center { + text-align: center; + } + .left { + text-align: left; + } + .table-data tr.footer-field td { + height:32pt; + vertical-align: middle; + } + @page { size: auto; margin: 0mm; } +`; diff --git a/src/core/print/PrintTemplate.ts b/src/core/print/PrintTemplate.ts new file mode 100644 index 00000000..e3d990f1 --- /dev/null +++ b/src/core/print/PrintTemplate.ts @@ -0,0 +1,65 @@ +import { PrintCss } from "./PrintStyles"; +import { PRINT_LOGO_TEXT, PRINT_LOGO } from "../../constants"; +import { GetPrintLogo, PrintLogoData } from "../utils/imageUtils"; + +export const GetPrintTemplate = ( + body: string, + title: string | null | undefined, + printOnLoad = false, + closeAfterPrint = true +) => { + return ` + + +${title ?? ""} + + + + ${body} + +`; +}; + +export const GetLogoSection = async () => { + let printLogoData: PrintLogoData | null = null; + if (PRINT_LOGO) { + try { + printLogoData = await GetPrintLogo(); + } catch (e) { + console.info(e); + } + } + return printLogoData || PRINT_LOGO_TEXT + ? ` + + ` + : ""; +}; + +export const GetHeaderSection = async () => { + const logoSection = await GetLogoSection(); + return ` +
+${logoSection} +
+ `; +}; diff --git a/src/core/print/printUtils.ts b/src/core/print/printUtils.ts new file mode 100644 index 00000000..c1196d14 --- /dev/null +++ b/src/core/print/printUtils.ts @@ -0,0 +1,19 @@ +const printDocumentInternal = (content: string) => { + const newWin = window.open("", "Print-Window"); + if (newWin) { + newWin.document.open(); + newWin.document.write(content); + newWin.document.close(); + // setTimeout(function () { + // if (newWin) { + // newWin.close(); + // } + // }, 10); + } +}; + +export const printDocument = (content: string) => { + setTimeout(() => { + printDocumentInternal(content); + }, 300); +}; diff --git a/src/core/utils/imageUtils.ts b/src/core/utils/imageUtils.ts new file mode 100644 index 00000000..809a1148 --- /dev/null +++ b/src/core/utils/imageUtils.ts @@ -0,0 +1,82 @@ +import { URL_PRINT_LOGO } from "../../constants"; + +export interface PrintLogoData { + image: string; + isSvg: boolean; +} + +export const GetPrintLogo = (): Promise => { + const printLogoUrl: string = URL_PRINT_LOGO(); + return new Promise((resolve, reject) => { + if (!printLogoUrl) { + resolve(null); + return; + } + + const hdnTxtPrintLogo = document.getElementById( + "hdnTxtPrintLogo" + ) as HTMLTextAreaElement; + if (hdnTxtPrintLogo && hdnTxtPrintLogo.value?.length > 0) { + const isSvg = hdnTxtPrintLogo.getAttribute("isSvg"); + resolve({ + image: (hdnTxtPrintLogo as HTMLTextAreaElement).value, + isSvg: isSvg === "1", + }); + return; + } + + if (printLogoUrl.toLowerCase().endsWith(".svg")) { + fetch(printLogoUrl, { + headers: { + "Disable-WWW-Authenticate": "true", + }, + }) + .then((response) => { + if (!response.ok) { + throw new Error("Print logo fetch network response was not OK"); + } + return response.text(); + }) + .then((svg) => { + if (hdnTxtPrintLogo) { + hdnTxtPrintLogo.value = svg; + hdnTxtPrintLogo.setAttribute("isSvg", "1"); + } + resolve({ image: svg, isSvg: true }); + }) + .catch((error) => { + console.error("Error fetching print logo: ", error); + reject("Error fetching print logo"); + }); + } else { + const img = new Image(); + const onImageLoaded = () => { + try { + const canvas = document.createElement("canvas"); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext("2d"); + if (ctx) { + ctx.drawImage(img, 0, 0); + const dataURL = canvas.toDataURL("image/png"); + if (hdnTxtPrintLogo) { + hdnTxtPrintLogo.value = dataURL; + hdnTxtPrintLogo.setAttribute("isSvg", "0"); + } + resolve({ image: dataURL, isSvg: false }); + } else { + reject("Failed to allocate canvas context for print logo"); + } + } catch (e) { + reject(e); + } + }; + const onImageLoadError = (ev: ErrorEvent) => { + reject(ev); + }; + img.addEventListener("load", onImageLoaded); + img.addEventListener("error", onImageLoadError); + img.src = printLogoUrl; + } + }); +}; diff --git a/src/stock-items/add-stock-item/add-stock-item.component.tsx b/src/stock-items/add-stock-item/add-stock-item.component.tsx index cc948e42..86269575 100644 --- a/src/stock-items/add-stock-item/add-stock-item.component.tsx +++ b/src/stock-items/add-stock-item/add-stock-item.component.tsx @@ -6,7 +6,6 @@ import PackagingUnits from "./packaging-units/packaging-units.component"; import Transactions from "./transactions/transactions.component"; import BatchInformation from "./batch-information/batch-information.component"; import StockQuantities from "./quantities/quantities.component"; -import StockRules from "./stock-rules/stock-rules.component"; import VerticalTabs from "../../core/components/tabs/vertical-tabs.component"; import { StockItemDTO } from "../../core/api/types/stockItem/StockItem"; import { SaveStockItem } from "../types"; diff --git a/src/stock-items/stock-items-table.component.tsx b/src/stock-items/stock-items-table.component.tsx index 9c2a02a4..afc93338 100644 --- a/src/stock-items/stock-items-table.component.tsx +++ b/src/stock-items/stock-items-table.component.tsx @@ -2,7 +2,6 @@ import { Button, DataTable, DataTableSkeleton, - Link, Pagination, Table, TableBatchActions, diff --git a/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx b/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx index 3c1b5c80..28e55f93 100644 --- a/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx +++ b/src/stock-operations/stock-operation-reason-selector/stock-operation-reason-selector.component.tsx @@ -4,7 +4,6 @@ import { Concept } from "../../core/api/types/concept/Concept"; import { ComboBox, SelectSkeleton } from "@carbon/react"; import { useConceptById } from "../../stock-lookups/stock-lookups.resource"; import { STOCK_ADJUSTMENT_REASON_CODED_CONCEPT_ID } from "../../constants"; -import { StockOperationDTO } from "../../core/api/types/stockOperation/StockOperationDTO"; interface StockOperationReasonSelectorProps { reasonUuid?: string; diff --git a/src/stock-operations/stock-operation.utils.tsx b/src/stock-operations/stock-operation.utils.tsx index 8cc2d4f6..ef1f5d6b 100644 --- a/src/stock-operations/stock-operation.utils.tsx +++ b/src/stock-operations/stock-operation.utils.tsx @@ -14,7 +14,6 @@ import { import AddStockOperation from "./add-stock-operation/add-stock-operation.component"; import { StockOperationType } from "../core/api/types/stockOperation/StockOperationType"; import { useLocation } from "react-router-dom"; -import { boolean } from "zod"; export const addOrEditStockOperation = async ( stockOperation: StockOperationDTO, diff --git a/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx b/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx index a41da5cd..7757bc5e 100644 --- a/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx +++ b/src/stock-operations/stock-operations-dialog/stock-operations-print-button.component.tsx @@ -1,10 +1,24 @@ -import React, { useCallback, useMemo, useState } from "react"; +import React from "react"; import { Button } from "@carbon/react"; -import { showModal } from "@openmrs/esm-framework"; +import { useConfig } from "@openmrs/esm-framework"; import { useTranslation } from "react-i18next"; import { Printer } from "@carbon/react/icons"; import { StockOperationDTO } from "../../core/api/types/stockOperation/StockOperationDTO"; +import { StockOperationItemCost } from "../../core/api/types/stockOperation/StockOperationItemCost"; +import { StockItemInventory } from "../../core/api/types/stockItem/StockItemInventory"; + +import { StockItemInventoryFilter } from "../../stock-items/stock-items.resource"; +import { ResourceRepresentation } from "../../core/api/api"; +import { BuildStockOperationData } from "../stock-print-reports/StockOperationReport"; +import { PrintGoodsReceivedNoteStockOperation } from "../stock-print-reports/GoodsReceivedNote"; +import { PrintTransferOutStockOperation } from "../stock-print-reports/StockTransferDocument"; +import { PrintRequisitionStockOperation } from "../stock-print-reports/RequisitionDocument"; +import { + getStockItemInventory, + getStockOperation, + getStockOperationItemsCost, +} from "../stock-operations.resource"; interface StockOperationCancelButtonProps { operation: StockOperationDTO; @@ -14,17 +28,127 @@ const StockOperationPrintButton: React.FC = ({ operation, }) => { const { t } = useTranslation(); - const launchPrintModal = useCallback(() => { - const dispose = showModal("stock-operation-dialog", { - title: "Print", - operation: operation, - closeModal: () => dispose(), - }); - }, [operation]); + + const { config } = useConfig(); + + // const { printItemCost, printBalanceOnHand } = config; + + // on print stock operation + const onPrintStockOperation = async () => { + try { + let parentOperation: StockOperationDTO | null | undefined; + let itemsCost: StockOperationItemCost[] | null | undefined = null; + let itemInventory: StockItemInventory[] | null | undefined = null; + + if (operation.requisitionStockOperationUuid) { + // get stock operation + getStockOperation(operation.requisitionStockOperationUuid) + .then((payload: any) => { + if ((payload as any).error) { + return; + } + parentOperation = payload; + }) + .catch((error: any) => { + if ((error as any).error) { + return; + } + return; + }); + if (!parentOperation) { + return null; + } + } + + if ( + parentOperation || + parentOperation?.operationType === "stockissue" || + parentOperation?.operationType === "transferout" + ) { + const enableOperationPrintCosts = true; + if (enableOperationPrintCosts) { + const inventoryFilter: StockItemInventoryFilter = {}; + if (operation?.uuid) { + inventoryFilter.stockOperationUuid = operation.uuid; + } + getStockOperationItemsCost(inventoryFilter) + .then((payload: any) => { + if ((payload as any).error) { + return; + } + itemsCost = payload?.results; + }) + .catch((error: any) => { + if ((error as any).error) { + return; + } + return; + }); + } + } + const enableBalance = true; + if ( + enableBalance && + (parentOperation || parentOperation?.operationType === "requisition") + ) { + const inventoryFilter: StockItemInventoryFilter = {}; + if (operation?.uuid) { + inventoryFilter.locationUuid = operation.atLocationUuid; + inventoryFilter.stockOperationUuid = operation.uuid; + } else { + inventoryFilter.locationUuid = operation.atLocationUuid; + inventoryFilter.stockOperationUuid = operation.uuid; + } + inventoryFilter.v = ResourceRepresentation.Default; + inventoryFilter.groupBy = "LocationStockItem"; + inventoryFilter.includeStockItemName = "true"; + + inventoryFilter.date = JSON.stringify( + parentOperation?.dateCreated ?? operation?.dateCreated + ); + + // get stock item inventory + getStockItemInventory(inventoryFilter) + .then((payload: any) => { + if ((payload as any).error) { + return; + } + itemInventory = payload?.results; + }) + .catch((error: any) => { + if ((error as any).error) { + return; + } + return; + }); + } + + const data = await BuildStockOperationData( + operation, + operation.stockOperationItems, + parentOperation, + itemsCost, + itemInventory + ); + if (data) { + if (operation?.operationType === "receipt") { + await PrintGoodsReceivedNoteStockOperation(data); + } else if (operation?.operationType === "transferout") { + await PrintTransferOutStockOperation(data); + } else { + await PrintRequisitionStockOperation(data); + } + } else { + console.info(data); + } + } catch (e) { + console.info(e); + } + }; return (