How to use po method in wpt

Best JavaScript code snippet using wpt

create-inv-po-two.js

Source:create-inv-po-two.js Github

copy

Full Screen

1import React, { Component } from "react";2import { findDOMNode } from "react-dom";3import _ from "lodash";4import ApiService from "../../libs/ApiService";5import Autosuggest from "react-autosuggest";6import { throws } from "assert";7import BlockUi from "react-block-ui";8import Router from "next/router";9import DataTable from "datatables.net-bs4";10import {11 asyncContainer,12 Typeahead13} from "../../libs/react-bootstrap-typeahead";14import ReactTooltip from "react-tooltip";15import ModalAlert from "../modalAlert";16const AsyncTypeahead = asyncContainer(Typeahead);17const poSearchApiUrl =18 "/api/purchaseorders?bypass=true&statuses=Confirmed&purchaseOrderNumber=";19const Api = new ApiService();20const poItemColumnPattern = [21 { data: "selected" },22 { data: "poItemNo" },23 { data: "materialDescription" },24 { data: "poItemQtyRemaining" },25 { data: "poItemQtyInitial" },26 { data: "unitDescription" },27 { data: "poItemUnitPrice" },28 { data: "amount" },29 { data: "poItemUnitPriceCurrency" }30];31class createInvoiceByPoStepTwo extends Component {32 constructor(props) {33 super(props);34 this.state = {35 mainPO: "",36 isReadyToNext: false,37 selectedPOItems: [],38 populatedPO: [],39 activePO: 0,40 AllPO: [],41 inputPONumber: "",42 isAddPOInputTriggered: false,43 activePOItems: [],44 poSuggestions: [],45 //Element46 poItemDataTable: undefined,47 //NewPOModal48 po_code: "",49 po_name: "",50 po_taxId: "",51 po_tel: "",52 po_branch: "",53 po_address: "",54 //POItemsTable55 isPurchaseOrderSelected: false,56 isQtyExceeded: false,57 selectedPOItems: {},58 blocking: false,59 isMainPOSelected: false,60 maximumQtyTooltip: "-",61 maximumQty: 0,62 alertModalAlertTitle: "",63 isAlertModalVisible: false,64 buttonAlert: [],65 isTextOnly: false,66 alertModalMsg: []67 };68 }69 routeBack() {70 Router.push("/invoice-detail?linearId=" + this.state.linearId);71 }72 toggleBlocking() {73 this.setState({ blocking: !this.state.blocking });74 }75 async componentDidMount() {76 if (this.props.mainState.stepOneProp === undefined) {77 this.renderPOItemTable([]);78 } else if (79 this.props.mainState.stepOneProp &&80 this.props.mainState.stepOneProp.selectedPO &&81 this.props.mainState.stepTwoProp === undefined &&82 this.props.mainState.stepThreeProp === undefined83 ) {84 await this.renderPOItemTable([]);85 await this.handlePOAutoCompleteChange(86 this.props.mainState.stepOneProp.selectedPO87 );88 const AllPO = [this.props.mainState.stepOneProp.selectedPO];89 this.setState({ AllPO: AllPO });90 } else {91 this.setState(92 {93 selectedPOItems: this.props.mainState.stepTwoProp.selectedPOItems,94 populatedPO: this.props.mainState.stepTwoProp.populatedPO,95 activePO: this.props.mainState.stepTwoProp.activePO,96 AllPO: this.props.mainState.stepTwoProp.AllPO,97 inputPONumber: this.props.mainState.stepTwoProp.inputPONumber,98 activePOItems: this.props.mainState.stepTwoProp.activePOItems,99 poSuggestions: this.props.mainState.stepTwoProp.poSuggestions,100 //Element101 poItemDataTable: this.props.mainState.stepTwoProp.poItemDataTable,102 //NewPOModal103 po_code: this.props.mainState.stepTwoProp.po_code,104 po_name: this.props.mainState.stepTwoProp.po_name,105 po_taxId: this.props.mainState.stepTwoProp.po_taxId,106 po_tel: this.props.mainState.stepTwoProp.po_tel,107 po_branch: this.props.mainState.stepTwoProp.po_branch,108 po_address: this.props.mainState.stepTwoProp.po_address109 },110 () => {111 this.renderPOItemTable(this.state.activePOItems, this.state.activePO);112 this.validateQtyInput();113 this.resolveAllowToNext();114 }115 );116 }117 this.resolveAllowToNext();118 this.checkIsPurchaseOrderSelected();119 }120 resolveAllowToNext() {121 if (!this.state.isQtyExceeded && this.state.isPurchaseOrderSelected) {122 this.setState({123 isReadyToNext: true124 });125 } else {126 this.setState({127 isReadyToNext: false128 });129 }130 }131 updatePOItemsNumber(addingPONumber, count) {132 let populatedPO = this.state.populatedPO;133 let index = populatedPO.findIndex(item => {134 return item.poNumber === addingPONumber;135 });136 populatedPO[index].count = count;137 this.setState({138 populatedPO: populatedPO139 });140 }141 async getPOItems(poNumber, itemList) {142 this.setState({143 activePOItems: itemList,144 blocking: false145 });146 this.updatePOItemsNumber(poNumber, itemList.length);147 let selectedPOItems = this.state.selectedPOItems;148 let selectedPOItemsKeys = Object.keys(selectedPOItems);149 let rowSelected = [];150 if (selectedPOItemsKeys.includes(poNumber)) {151 rowSelected = selectedPOItems[poNumber].rowSelected;152 } else {153 selectedPOItems[this.state.activePO] = {154 rowSelected: [],155 item: []156 };157 this.setState({158 selectedPOItems: selectedPOItems159 });160 }161 let pointer = 0;162 let data = [];163 itemList.forEach(item => {164 if (this.props.mainState.stepOneProp.autoPopulate === true) {165 item.quantity.initial = item.quantity.remaining;166 } else {167 item.quantity.initial = 0;168 }169 let qty = 0;170 let unitPrice = item.poItemUnitPrice;171 if (poNumber in selectedPOItems) {172 if (rowSelected.length != 0) {173 if (rowSelected.includes(item.poItemNo)) {174 qty = selectedPOItems[poNumber].item[pointer].quantity.initial;175 unitPrice = selectedPOItems[poNumber].item[pointer].poItemUnitPrice;176 pointer++;177 } else {178 qty = item.quantity.initial;179 }180 } else {181 qty = item.quantity.initial;182 }183 }184 data.push({185 selected:186 '<div className="custom-control custom-checkbox">' +187 '<input type="checkbox" className="custom-control-input" id="select-' +188 item.poItemNo +189 '"></input>' +190 '<label className="custom-control-label pl-1 font-small text-shadow" for="selectall"></label>' +191 "</div>",192 poItemNo: item.poItemNo,193 materialDescription: item.materialDescription,194 poItemQtyRemaining: this.formatQtyNumber(item.quantity.remaining, true),195 poItemQtyInitial:196 '<input disabled type="text" ref="qty-ref-' +197 item.poItemNo +198 '" data-tip="custom show" data-event="focus" data-event-off="blur" data-for="maximumQty" id="qty-select-' +199 item.poItemNo +200 '" value="' +201 this.formatQtyNumber(qty) +202 '" class="form-control text-right"></input>',203 unitDescription: item.quantity.unit,204 poItemUnitPrice:205 "estimatedUnitPrice" in item206 ? '<input disabled type="text" id="unit-select-' +207 item.poItemNo +208 '" value="' +209 this.formatPriceNumber(unitPrice) +210 '" class="form-control text-right"></input>'211 : this.formatPriceNumber(unitPrice),212 amount:213 '<div id="amount-' +214 item.poItemNo +215 '">' +216 this.formatPriceNumber(qty * unitPrice) +217 "</div>",218 poItemUnitPriceCurrency: item.poItemUnitPriceCurrency219 });220 });221 this.state.poItemDataTable.clear();222 this.state.poItemDataTable.rows.add(data);223 this.state.poItemDataTable.draw();224 $("[id^='select-']")225 .parents("tr")226 .prop("className", "odd");227 rowSelected.forEach(row => {228 $("[id^='select-" + row + "']")229 .parents("tr")230 .prop("className", "odd active");231 $("[id^='select-" + row + "']").prop("checked", "checked");232 $("#qty-select-" + row).prop("disabled", false);233 $("#unit-select-" + row).prop("disabled", false);234 });235 await this.shouldPOItemsSelectAll();236 await this.validateQtyInput();237 }238 async renderPOItemTable(poItems, poNumber) {239 var data = [];240 let selectedPOItems = this.state.selectedPOItems;241 let pointer = 0;242 poItems.forEach((item, index) => {243 let qty = 0;244 let unitPrice = item.poItemUnitPrice;245 if (poNumber in selectedPOItems) {246 if (selectedPOItems[poNumber].rowSelected.length != 0) {247 if (selectedPOItems[poNumber].rowSelected.includes(item.poItemNo)) {248 qty = selectedPOItems[poNumber].item[pointer].quantity.initial;249 unitPrice = selectedPOItems[poNumber].item[pointer].poItemUnitPrice;250 pointer++;251 } else {252 qty = 0;253 }254 }255 }256 data.push({257 selected:258 '<div className="custom-control custom-checkbox">' +259 '<input type="checkbox" className="custom-control-input" id="select-' +260 item.poItemNo +261 '"></input>' +262 '<label className="custom-control-label pl-1 font-small text-shadow" for="selectall"></label>' +263 "</div>",264 poItemNo: item.poItemNo,265 materialDescription: item.materialDescription,266 poItemQtyRemaining: this.formatQtyNumber(item.quantity.remaining, true),267 poItemQtyInitial:268 '<input disabled type="text" ref="qty-ref-' +269 item.poItemNo +270 '" data-tip="custom show" data-event="focus" data-event-off="blur" data-for="maximumQty" id="qty-select-' +271 item.poItemNo +272 '" value="' +273 this.formatQtyNumber(qty) +274 '" class="form-control"></input>',275 unitDescription: item.quantity.unit,276 poItemUnitPrice:277 "estimatedUnitPrice" in item278 ? '<input disabled type="text" id="unit-select-' +279 item.poItemNo +280 '" value="' +281 this.formatPriceNumber(unitPrice) +282 '" class="form-control"></input>'283 : this.formatPriceNumber(unitPrice),284 amount:285 '<div id="amount-' +286 item.poItemNo +287 '">' +288 this.formatPriceNumber(qty * unitPrice) +289 "</div>",290 poItemUnitPriceCurrency: item.poItemUnitPriceCurrency291 });292 });293 var _this = this;294 var dts = $(_this.el)295 .DataTable({296 language: {297 lengthMenu: "Display _MENU_ Per Page"298 },299 scrollX: true,300 data: data,301 columns: poItemColumnPattern,302 createdRow: function(row, data, dataIndex) {303 if (dataIndex % 2 == 0) {304 $(row).addClass("even");305 } else $(row).addClass("odd");306 },307 columnDefs: [308 {309 targets: [0],310 sortable: false311 },312 {313 targets: [1],314 width: "120px",315 sortable: false316 },317 {318 targets: [2],319 width: "300px",320 sortable: false321 },322 {323 targets: [3],324 width: "150px",325 sortable: false326 },327 {328 targets: [4],329 width: "100px",330 sortable: false331 },332 {333 targets: [5],334 width: "150px",335 sortable: false336 },337 {338 targets: [6],339 width: "120px",340 sortable: false341 },342 {343 targets: [7, 8],344 width: "100px",345 sortable: false346 }347 ],348 stateSave: false,349 paging: false,350 bLengthChange: false,351 searching: false,352 info: false,353 ordering: false354 })355 .on("error", function(e, settings, techNote, message) {356 console.log("An error has been reported by DataTables: ", message);357 });358 $("[id^='select-']")359 .parents("tr")360 .prop("className", "odd");361 if (poItems.length > 0) {362 let selectedPOItemsKeys = Object.keys(selectedPOItems);363 let rowSelected = [];364 if (selectedPOItemsKeys.includes(poNumber)) {365 rowSelected = selectedPOItems[poNumber].rowSelected;366 } else {367 selectedPOItems[this.state.activePO] = {368 rowSelected: [],369 item: []370 };371 this.setState({372 selectedPOItems: selectedPOItems373 });374 }375 rowSelected.forEach(row => {376 $("[id^='select-" + row + "']")377 .parents("tr")378 .prop("className", "odd active");379 $("[id^='select-" + row + "']").prop("checked", "checked");380 $("#qty-select-" + row).prop("disabled", false);381 $("#unit-select-" + row).prop("disabled", false);382 });383 }384 await $("[id^='select-']").change(event => {385 this.handlePOItemSelect(event);386 });387 await $("#selectall").change(event => {388 this.handlePOItemSelect(event);389 });390 await $("[id^='qty-select-']").on("input", event => {391 this.handlePOItemQtyChange(event);392 });393 await $("[id^='qty-select-']").on("focus", event => {394 this.handlePOItemQtyFocus(event);395 });396 await $("[id^='qty-select-']").on("blur", event => {397 event.target.value = this.formatQtyNumber(398 this.formatNumberInput(event.target.value, 3)399 );400 // event.target.value = Number.parseFloat(401 // +this.formatNumberInput(event.target.value, 3)402 // ).toFixed(3);403 });404 await $("[id^='unit-select-']").on("input", event => {405 this.handlePOItemUnitPriceChange(event);406 });407 await $("[id^='unit-select-']").on("blur", event => {408 event.target.value = this.formatPriceNumber(409 this.formatNumberInput(event.target.value, 2)410 );411 // event.target.value = Number.parseFloat(412 // +this.formatNumberInput(event.target.value, 2)413 // ).toFixed(2);414 });415 await ReactTooltip.rebuild();416 this.setState({417 poItemDataTable: dts418 });419 this.checkIsPurchaseOrderSelected();420 }421 componentWillUnmount() {422 this.state = [];423 }424 handlePOItemQtyFocus(event) {425 if (event.target.value === "0.000") {426 event.target.value = "";427 }428 let arr = event.target.id.split("-");429 let itemRef = arr[2];430 ReactTooltip.show(findDOMNode(this.refs["qty-ref-" + itemRef]));431 let selectedPOItems = this.state.selectedPOItems;432 let targetItem = selectedPOItems[this.state.activePO].item.find(item => {433 return item.poItemNo === itemRef;434 });435 this.setState({436 maximumQtyTooltip: this.formatQtyNumber(437 targetItem.quantity.remaining +438 targetItem.overDeliveryQuantity.remaining439 ),440 maximumQty: (441 targetItem.quantity.remaining +442 targetItem.overDeliveryQuantity.remaining443 ).toFixed(3)444 });445 }446 async handlePOItemQtyChange(event) {447 event.target.value = this.formatNumberInput(448 event.target.value.replace(/[^0-9.]/gm, ""),449 3450 );451 let selectedPOItems = this.state.selectedPOItems;452 let changeQty = event.target.value;453 let arr = event.target.id.split("-");454 let itemRef = arr[2];455 let targetItem = selectedPOItems[this.state.activePO].item.find(item => {456 return item.poItemNo === itemRef;457 });458 targetItem.quantity.initial = +changeQty;459 $("#amount-" + targetItem.poItemNo).text(460 this.formatPriceNumber(+changeQty * targetItem.poItemUnitPrice)461 );462 this.validateQtyInput();463 await this.setState({464 selectedPOItems: selectedPOItems465 });466 }467 validateQtyInput() {468 let isQtyExceededFound = false;469 let selectedPOItems = this.state.selectedPOItems;470 let _this = this;471 _.forOwn(selectedPOItems, (value, key) => {472 let poItems = selectedPOItems[key].item;473 poItems.forEach((item, index) => {474 if (475 item.quantity.initial >476 item.quantity.remaining + item.overDeliveryQuantity.remaining ||477 item.quantity.initial === 0478 ) {479 if (key === _this.state.activePO) {480 $("#qty-select-" + item.poItemNo).css("color", "red");481 }482 isQtyExceededFound = true;483 } else {484 if (key === _this.state.activePO) {485 $("#qty-select-" + item.poItemNo).css("color", "");486 }487 }488 });489 });490 if (isQtyExceededFound) {491 this.setState(492 {493 isQtyExceeded: true494 },495 () => this.resolveAllowToNext()496 );497 } else {498 this.setState(499 {500 isQtyExceeded: false501 },502 () => this.resolveAllowToNext()503 );504 }505 }506 async handlePOItemUnitPriceChange(event) {507 event.target.value = this.formatNumberInput(508 event.target.value.replace(/[^0-9.]/gm, ""),509 2510 );511 let selectedPOItems = this.state.selectedPOItems;512 let changeUnit = event.target.value;513 let arr = event.target.id.split("-");514 let itemRef = arr[2];515 let targetItem = selectedPOItems[this.state.activePO].item.find(item => {516 return item.poItemNo === itemRef;517 });518 targetItem.poItemUnitPrice = +changeUnit;519 $("#amount-" + targetItem.poItemNo).text(520 this.formatPriceNumber(+changeUnit * targetItem.quantity.initial)521 );522 await this.setState({523 selectedPOItems: selectedPOItems524 });525 }526 async handlePOItemSelect(event) {527 let element = event.target;528 if (element.id === "selectall") {529 if (!element.checked) {530 $("[id^='select']").prop("checked", false);531 $("[id^='select-']")532 .parents("tr")533 .prop("className", "odd");534 $("[id^='qty-select-']").prop("disabled", true);535 $("[id^='unit-select-']").prop("disabled", true);536 } else {537 $("[id^='select']").prop("checked", true);538 $("[id^='select-']")539 .parents("tr")540 .prop("className", "odd active");541 $("[id^='qty-select-']").prop("disabled", false);542 $("[id^='unit-select-']").prop("disabled", false);543 }544 } else {545 this.shouldPOItemsSelectAll();546 if (element.checked) {547 $(event.originalEvent.target)548 .closest("tr")549 .addClass("active");550 $("#qty-" + element.id).prop("disabled", false);551 $("#unit-" + element.id).prop("disabled", false);552 } else {553 $(event.originalEvent.target)554 .closest("tr")555 .removeClass("active");556 $("#qty-" + element.id).prop("disabled", true);557 $("#unit-" + element.id).prop("disabled", true);558 }559 }560 let selectedPOItems = this.state.selectedPOItems;561 let selectedPOItemsActivePO = [];562 let poItems = this.state.activePOItems;563 let rowSelected = [];564 poItems.forEach((item, index) => {565 if ($("#select-" + item.poItemNo)[0].checked) {566 let qtyFieldValue = $("#qty-select-" + item.poItemNo).val();567 qtyFieldValue = parseInt(qtyFieldValue.replace(",", ""));568 let selectedItem = selectedPOItems[this.state.activePO].item.find(569 sItem => {570 return sItem.poItemNo == item.poItemNo;571 }572 );573 let addingItem = selectedItem === undefined ? item : selectedItem;574 addingItem.quantity.initial = +qtyFieldValue;575 rowSelected.push(576 selectedItem === undefined ? item.poItemNo : selectedItem.poItemNo577 );578 selectedPOItemsActivePO.push(addingItem);579 }580 });581 selectedPOItems[this.state.activePO] = {582 rowSelected: rowSelected,583 item: selectedPOItemsActivePO584 };585 await this.setState(586 {587 selectedPOItems: selectedPOItems588 },589 () => {590 this.validateQtyInput();591 }592 );593 await this.checkIsPurchaseOrderSelected();594 }595 handleInputChange(event) {596 this.setState({597 [event.target.name]: event.target.value598 });599 }600 handlePOAutoCompleteChange(selectedPO) {601 if (selectedPO !== undefined) {602 let existingPO = this.state.populatedPO.find(po => {603 return po.poNumber === selectedPO.purchaseOrderNumber;604 });605 if (existingPO === undefined) {606 const AllPO = this.state.AllPO;607 AllPO.push(selectedPO);608 this.setState(609 {610 AllPO: AllPO,611 inputPONumber: selectedPO.purchaseOrderNumber612 },613 () => this.handleSubmitNewPO(selectedPO)614 );615 } else {616 this.setState({617 inputPONumber: ""618 });619 }620 }621 }622 handleSubmitNewPO(selectedPO) {623 let newPONumber = this.state.inputPONumber;624 if (this.checkPOConfiguration(newPONumber)) {625 let populatedPO = this.state.populatedPO;626 let newPOTemplate = {627 poNumber: this.state.inputPONumber,628 count: "-",629 vendorTaxNumber: selectedPO.vendorTaxNumber,630 businessPlaceTaxNumber: selectedPO.businessPlaceTaxNumber,631 companyBranchCode: selectedPO.companyBranchCode,632 paymentTermCode: selectedPO.paymentTermCode,633 paymentTermDays: selectedPO.paymentTermDays634 };635 this.handleSelectPO(newPONumber);636 populatedPO.push(newPOTemplate);637 this.setState({638 populatedPO: populatedPO,639 inputPONumber: "",640 isAddPOInputTriggered: false,641 isMainPOSelected: true642 });643 } else {644 this.setState({645 alertModalAlertTitle: "Error",646 isAlertModalVisible: true,647 buttonAlert: [648 {649 label: "Close",650 attribute: {651 className: "btn btn--transparent btn-wide",652 onClick: this.handleDismissBtnModal653 }654 }655 ],656 isTextOnly: true,657 alertModalMsg: ["The system cannot get attachment configuration."]658 });659 }660 }661 handleSelectPO(poNumber) {662 this.setState({663 blocking: true664 });665 Api.getPOByPONumber(poNumber).then(res => {666 let po = res.data.find(po => {667 return po.purchaseOrderNumber === poNumber;668 });669 Api.getPOItemsByPOId(po.linearId)670 .then(res => {671 let itemList = this.filterItemByCondition(res.data);672 return itemList;673 })674 .then(itemList => {675 if (itemList.length > 0) {676 this.getPOItems(poNumber, itemList);677 } else {678 this.setState({679 alertModalAlertTitle: "Error",680 isAlertModalVisible: true,681 buttonAlert: [682 {683 label: "Close",684 attribute: {685 className: "btn btn--transparent btn-wide",686 onClick: this.handleDismissBtnModal687 }688 }689 ],690 isTextOnly: true,691 alertModalMsg: [692 "There is no available item for invoice creation.",693 <br />,694 "Please contact procurement team for further assistant."695 ]696 });697 this.handleRemovePO(poNumber);698 }699 })700 .then(() => {701 $("[id^='select-']").change(event => {702 this.handlePOItemSelect(event);703 });704 $("[id^='qty-select-']").on("input", event => {705 this.handlePOItemQtyChange(event);706 //$(event.target).css("width", event.target.value.length * 14);707 });708 $("[id^='qty-select-']").on("focus", event => {709 this.handlePOItemQtyFocus(event);710 if (event.target.value == "0.000") {711 event.target.value = "";712 }713 // $(event.target).css("width", event.target.value.length * 14);714 });715 $("[id^='qty-select-']").on("blur", event => {716 this.handlePOItemQtyFocus(event);717 if (event.target.value == "" || event.target.value == "0.000") {718 event.target.value = "0.000";719 }720 });721 $("[id^='unit-select-']").on("input", event => {722 this.handlePOItemUnitPriceChange(event);723 //$(event.target).css("width", event.target.value.length * 14);724 });725 $("[id^='unit-select-']").on("blur", event => {726 event.target.value = Number.parseFloat(727 +this.formatNumberInput(event.target.value, 2)728 ).toFixed(2);729 //$(event.target).css("width", event.target.value.length * 12);730 });731 ReactTooltip.rebuild();732 });733 this.setState({734 activePO: poNumber735 // blocking: false736 });737 });738 }739 handleRemovePO(poNumber) {740 let populatedPO = this.state.populatedPO;741 let selectedPOItems = this.state.selectedPOItems;742 populatedPO.splice(743 populatedPO.findIndex(po => po.poNumber === poNumber),744 1745 );746 delete selectedPOItems[poNumber];747 this.setState(748 {749 populatedPO: populatedPO,750 selectedPOItems: selectedPOItems751 },752 () => {753 if (this.state.populatedPO.length > 0) {754 if (this.state.activePO === poNumber) {755 this.handleSelectPO(this.state.populatedPO[0].poNumber);756 }757 } else {758 this.state.poItemDataTable.clear();759 this.state.poItemDataTable.rows.add([]);760 this.state.poItemDataTable.draw();761 this.setState({762 isMainPOSelected: false763 });764 }765 this.checkIsPurchaseOrderSelected();766 }767 );768 }769 handleAddPOButton() {770 this.setState({771 isAddPOInputTriggered: true772 });773 }774 handleDismissBtnModal = () => {775 this.setState({776 blocking: false,777 alertModalAlertTitle: "",778 isAlertModalVisible: false,779 buttonAlert: [],780 isTextOnly: true,781 alertModalMsg: []782 });783 };784 shouldPOItemsSelectAll() {785 let isSelectAll = true;786 let poItems = this.state.activePOItems;787 poItems.forEach(item => {788 if (!$("#select-" + item.poItemNo)[0].checked) {789 $("#selectall").prop("checked", false);790 isSelectAll = false;791 }792 if (isSelectAll) {793 $("#selectall").prop("checked", true);794 }795 });796 }797 async checkPOConfiguration(PO) {798 let isValid = false;799 Api.getPOByPONumber(PO)800 .then(res => {801 return res.data.find(po => {802 return po.purchaseOrderNumber === PO;803 });804 })805 .then(po => {806 Api.getInvoiceConfiguration(807 po.buyer.legalName,808 po.businessPlaceTaxNumber,809 po.vendorTaxNumber810 ).then(config => {811 if (812 Object.keys(config).length === 0 ||813 !"attachmentConfiguration" in config814 ) {815 isValid = false;816 } else {817 isValid = true;818 }819 });820 })821 .then(() => {822 return isValid;823 });824 }825 async checkIsPurchaseOrderSelected() {826 let isSelected = false;827 let editedPO = Object.keys(this.state.selectedPOItems);828 for (let i = 0; i < editedPO.length; i++) {829 if (this.state.selectedPOItems[editedPO[i]].item.length > 0) {830 isSelected = true;831 }832 }833 this.setState(834 {835 isPurchaseOrderSelected: isSelected836 },837 () => this.resolveAllowToNext()838 );839 }840 filterItemByCondition(poItems) {841 return poItems.filter(item => {842 return item.deleteFlag !== "BLOCKED" && item.deleteFlag !== "DELETED";843 });844 }845 onSuggestionsFetchRequested = ({ value }) => {846 let suggestionArray = [];847 let inputValue = value.trim().toLowerCase();848 let inputLength = inputValue.length;849 suggestionArray =850 inputLength < 3851 ? []852 : this.state.AllPO.filter(853 po =>854 po.purchaseOrderNumber.toLowerCase().slice(0, inputLength) ===855 inputValue856 );857 this.setState({858 poSuggestions: suggestionArray859 });860 };861 onSuggestionsClearRequested = () => {862 this.setState({863 poSuggestions: []864 });865 };866 ///// NEXT //////867 async handleNext() {868 let allSelectedPO = Object.keys(this.state.selectedPOItems);869 await this.setState({870 mainPO: this.state.selectedPOItems[allSelectedPO[0]]871 });872 await this.props.updateState(this.state);873 this.props.nextStep();874 }875 async handleBack() {876 await this.props.updateState(this.state);877 this.props.previousStep();878 }879 routeCancel() {880 Router.push("/invoice");881 }882 ///// Util /////883 formatQtyNumber(amount, grouping) {884 return Intl.NumberFormat("th-TH", {885 useGrouping: grouping,886 minimumFractionDigits: 3,887 maximumFractionDigits: 3888 }).format(amount);889 }890 formatPriceNumber(amount, grouping) {891 return Intl.NumberFormat("th-TH", {892 useGrouping: grouping,893 minimumFractionDigits: 2,894 maximumFractionDigits: 2895 }).format(amount);896 }897 formatNumberInput(input, decimal) {898 let valueReplace = input.replace(/[^0-9.]/gm, "");899 let valueSplit = valueReplace.split(".");900 let interger = valueSplit[0];901 let fraction = "";902 if (input.endsWith(".")) {903 fraction =904 valueSplit[1] === "" ? "." : "." + valueSplit[1].substring(0, decimal);905 } else {906 fraction =907 valueSplit[1] === undefined908 ? ""909 : "." + valueSplit[1].substring(0, decimal);910 }911 return fraction === ""912 ? interger.replace("-", "")913 : (interger + fraction).replace("-", "");914 }915 render() {916 return (917 <BlockUi tag="div" blocking={this.state.blocking}>918 <ReactTooltip id="maximumQty" place="top" type="light" effect="float">919 <span>920 <b>Maximum: {this.state.maximumQtyTooltip}</b>921 </span>922 </ReactTooltip>923 <div>924 <div id="invoice_create" class="row">925 <div id="step-indicator" className="col-12">926 <ul className="d-flex justify-content-center">927 <li className="flex-fill finished">928 <div className="indicator step-1 rounded-circle text-center">929 <span className="number">1</span>930 <i className="fa fa-check" />931 </div>932 <p className="text-center">Select Type of Invoice</p>933 </li>934 <li className="flex-fill active">935 <div className="indicator step-2 rounded-circle text-center">936 <span className="number">2</span>937 <i className="fa fa-check" />938 </div>939 <p className="text-center">Select Items</p>940 </li>941 <li className="flex-fill">942 <div className="indicator step-3 rounded-circle text-center">943 <span className="number">3</span>944 <i className="fa fa-check" />945 </div>946 <p className="text-center">Insert Invoice Details</p>947 </li>948 <li className="flex-fill">949 <div className="indicator step-4 rounded-circle text-center">950 <span className="number">4</span>951 <i className="fa fa-check" />952 </div>953 <p className="text-center">Summary</p>954 </li>955 </ul>956 </div>957 <div class="page__header col-12">958 <h2>Please Select PO and PO Items (PO)</h2>959 {/* <a960 href="https://support.b2p.in"961 id="btnHelp"962 target="_blank"963 data-toggle="tooltip"964 data-placement="bottom"965 title="Help!"966 >967 <i class="fa fa-question-circle" />968 </a> */}969 </div>970 <div id="editForm" name="editForm" class="form col-12">971 <div class="box box--width-header col-12">972 <div class="d-flex">973 <div974 id="selectPO-panel"975 class="col-2 border-right border-1px border-lightgrey px-0"976 >977 <div id="po_items" class="col-12 pb-3 px-0">978 {/* <div style={{ 'padding': '0px' }} class="form-group border-bottom border-1px border-lightgrey">979 </div> */}980 <ul>981 {982 <div983 style={{ marginTop: "15px", position: "relative" }}984 >985 {this.state.populatedPO.length > 0986 ? this.state.populatedPO.map(po => (987 <li988 className={989 this.state.activePO === po.poNumber990 ? "active"991 : "inactive"992 }993 >994 <a995 href="javascript:void(0);"996 onClick={() =>997 this.handleSelectPO(po.poNumber)998 }999 >1000 <strong className="gray-1 text-center font-bold">1001 {" "}1002 {po.poNumber}1003 <br /> ({po.count}{" "}1004 {po.count > 1 ? "items" : "item"}){" "}1005 </strong>1006 </a>1007 {this.state.populatedPO.length > 1 ? (1008 <i1009 onClick={() =>1010 this.handleRemovePO(po.poNumber)1011 }1012 className="fa fa-times gray-2"1013 />1014 ) : (1015 ""1016 )}1017 </li>1018 ))1019 : ""}1020 </div>1021 }1022 {this.state.populatedPO.length > 0 ? (1023 <div>1024 {this.state.isAddPOInputTriggered === false ? (1025 <div style={{ marginTop: "20px" }}>1026 <p1027 hidden={1028 this.state.isAddPOInputTriggered === true1029 }1030 id="morePOBtn"1031 class="text-center"1032 >1033 <a1034 href="javascript:void(0);"1035 class="text-bold"1036 onClick={() => this.handleAddPOButton()}1037 >1038 <i class="fa fa-plus small" /> Add PO1039 </a>1040 </p>1041 </div>1042 ) : (1043 <div className="form-label-group px-3">1044 <AsyncTypeahead1045 inputProps={{1046 id: `purchaseOrderNumber`,1047 name: `purchaseOrderNumber`,1048 className: `input-search`,1049 title: `Select PO.`1050 }}1051 ref={Typeahead =>1052 (this.Typeahead = Typeahead)1053 }1054 placeholder="Select PO."1055 defaultInputValue=""1056 isLoading={this.state.isLoading}1057 labelKey="purchaseOrderNumber"1058 minLength={3}1059 useCache={false}1060 onChange={selected =>1061 this.handlePOAutoCompleteChange(selected[0])1062 }1063 onSearch={query => {1064 if (query.trim() != "") {1065 let fetchURL = "";1066 this.setState({ isLoading: true });1067 if (this.state.isMainPOSelected) {1068 fetchURL =1069 `${poSearchApiUrl}${query}&vendorTaxNumber=${1070 this.state.populatedPO[0]1071 .vendorTaxNumber1072 }&` +1073 `businessPlaceTaxNumber=${1074 this.state.populatedPO[0]1075 .businessPlaceTaxNumber1076 }&` +1077 `companyBranchCode=${1078 this.state.populatedPO[0]1079 .companyBranchCode1080 }&` +1081 `paymentTermCode=${1082 this.state.populatedPO[0]1083 .paymentTermCode1084 }`;1085 } else {1086 fetchURL = `${poSearchApiUrl}${query}`;1087 }1088 fetch(fetchURL)1089 .then(resp => resp.json())1090 .then(json => {1091 this.setState({1092 isLoading: false,1093 options: json.data1094 });1095 });1096 }1097 }}1098 options={this.state.options}1099 />1100 </div>1101 )}1102 </div>1103 ) : (1104 <div className="form-label-group px-3">1105 <AsyncTypeahead1106 inputProps={{1107 id: `purchaseOrderNumber`,1108 name: `purchaseOrderNumber`,1109 className: `input-search`,1110 title: `Select PO.`1111 }}1112 ref={Typeahead => (this.Typeahead = Typeahead)}1113 placeholder="Select PO."1114 defaultInputValue=""1115 isLoading={this.state.isLoading}1116 labelKey="purchaseOrderNumber"1117 minLength={3}1118 useCache={false}1119 onChange={selected =>1120 this.handlePOAutoCompleteChange(selected[0])1121 }1122 onSearch={query => {1123 if (query.trim() != "") {1124 let fetchURL = "";1125 this.setState({ isLoading: true });1126 if (this.state.isMainPOSelected) {1127 fetchURL =1128 `${poSearchApiUrl}${query}&vendorTaxNumber=${1129 this.state.populatedPO[0]1130 .vendorTaxNumber1131 }&` +1132 `businessPlaceTaxNumber=${1133 this.state.populatedPO[0]1134 .businessPlaceTaxNumber1135 }&` +1136 `companyBranchCode=${1137 this.state.populatedPO[0]1138 .companyBranchCode1139 }&` +1140 `paymentTermCode=${1141 this.state.populatedPO[0]1142 .paymentTermCode1143 }`;1144 } else {1145 fetchURL = `${poSearchApiUrl}${query}`;1146 }1147 fetch(fetchURL)1148 .then(resp => resp.json())1149 .then(json => {1150 this.setState({1151 isLoading: false,1152 options: json.data1153 });1154 });1155 }1156 }}1157 options={this.state.options}1158 />1159 </div>1160 )}1161 {this.state.populatedPO.length === 0 ? (1162 <div className="px-3">1163 <p class="text-center">1164 {" "}1165 Start selecting items by adding PO{" "}1166 </p>1167 <p class="text-center">1168 {" "}1169 *Only approved PO can be selected here{" "}1170 </p>1171 </div>1172 ) : (1173 ""1174 )}1175 </ul>1176 </div>1177 </div>1178 <div id="POLists" class="col-10">1179 <div class="table-responsive mt-3">1180 <table class="table datatable" ref={el => (this.el = el)}>1181 <thead class="rounded">1182 <tr>1183 <th width="50">1184 <div class="custom-control custom-checkbox">1185 <input1186 type="checkbox"1187 class="custom-control-input"1188 id="selectall"1189 />1190 <label1191 class="custom-control-label pl-1 font-small text-shadow"1192 for="selectall"1193 />1194 {}1195 </div>1196 </th>1197 <th width="100">1198 PO Item <br />1199 No.1200 </th>1201 <th>Material Description</th>1202 <th width="100">1203 Remaining <br />1204 QTY1205 </th>1206 <th width="50">QTY</th>1207 <th width="100">1208 Unit <br />1209 Description1210 </th>1211 <th width="90">Unit Price</th>1212 <th width="90">Amount</th>1213 <th width="90">Currency</th>1214 </tr>1215 </thead>1216 </table>1217 {/* <label id='label-error' style={{ 'color': 'red', 'margin-left': '10px', 'float': 'right', 'padding-right': '10px' }} hidden={!this.state.isQtyExceeded}>Qty cannot greater than remainning qty.</label>1218 <label id='label-error' style={{ 'color': 'red', 'margin-left': '10px', 'float': 'right', 'padding-right': '10px' }} hidden={this.state.isPurchaseOrderSelected}>Please select Purchase Order Item.</label> */}1219 </div>1220 </div>1221 </div>1222 </div>1223 <div class="row">1224 <div class="col-12 text-center">1225 <button1226 type="button"1227 name="btnCancel"1228 id="btnCancel"1229 class="btn btn--transparent btn-wide"1230 data-toggle="modal"1231 data-target="#cancelWarning"1232 >1233 Cancel1234 </button>1235 <button1236 type="button"1237 name="btnBack"1238 id="btnBack"1239 onClick={() => this.handleBack()}1240 class="btn btn--transparent btn-wide"1241 >1242 <i class="fa fa-chevron-left" /> Back1243 </button>1244 {this.state.isReadyToNext === true ? (1245 <button1246 type="button"1247 name="btnNext"1248 id="btnNext"1249 onClick={() => this.handleNext()}1250 class="btn btn-wide"1251 >1252 Next <i class="fa fa-chevron-right" />1253 </button>1254 ) : (1255 <button1256 disabled1257 type="button"1258 name="btnNext"1259 id="btnNext"1260 class="btn btn-wide"1261 >1262 Next <i class="fa fa-chevron-right" />1263 </button>1264 )}1265 {/* <button type="button" name="btnNext" id="btnNext" onClick={this.props.nextStep} class="btn btn--transparent btn-purple">Next <i class="fa fa-chevron-right"></i></button> */}1266 </div>1267 </div>1268 <div class="row">&nbsp;</div>1269 </div>1270 <div1271 id="configWarning"1272 class="modal hide fade"1273 tabindex="-1"1274 role="dialog"1275 aria-labelledby="addPO"1276 aria-hidden="true"1277 >1278 <div class="modal-dialog modal-lg" role="document">1279 <div class="modal-content">1280 <div class="modal-header">1281 <h3 id="myModalLabel">1282 <center>Error</center>1283 </h3>1284 </div>1285 <div class="modal-body d-flex">1286 <center>1287 The system cannot get attachment configuration1288 </center>1289 </div>1290 <div class="modal-footer justify-content-center">1291 <button1292 type="button"1293 name="btnCloseModal"1294 id="btnCloseModal"1295 class="btn btn--transparent btn-wide"1296 data-dismiss="modal"1297 aria-hidden="true"1298 >1299 OK1300 </button>1301 </div>1302 </div>1303 </div>1304 </div>1305 <div1306 id="itemConditionWarning"1307 class="modal hide fade"1308 tabindex="-1"1309 role="dialog"1310 aria-labelledby="addPO"1311 aria-hidden="true"1312 >1313 <div class="modal-dialog" role="document">1314 <div class="modal-content">1315 <div class="modal-header">1316 <h3 id="myModalLabel">1317 <center>Error</center>1318 </h3>1319 </div>1320 <div class="modal-body text-center">1321 <div className="text">1322 There is no available item for invoice creation. Please1323 contact procurement team for further assistant.1324 </div>1325 </div>1326 <div class="modal-footer justify-content-center">1327 <button1328 type="button"1329 name="btnCloseModal"1330 id="btnCloseModal"1331 class="btn btn--transparent btn-wide"1332 data-dismiss="modal"1333 aria-hidden="true"1334 >1335 Close1336 </button>1337 </div>1338 </div>1339 </div>1340 </div>1341 <div1342 id="cancelWarning"1343 class="modal hide fade"1344 tabindex="-1"1345 role="dialog"1346 aria-labelledby="cancel"1347 aria-hidden="true"1348 >1349 <div class="modal-dialog modal-sm" role="document">1350 <div class="modal-content">1351 <div class="modal-header">1352 <h3 id="myModalLabel" class="w-100 text-center">1353 Cancel1354 </h3>1355 </div>1356 <div class="modal-body d-flex justify-content-center">1357 <div className="text">1358 Do you want to cancel this invoice?1359 </div>1360 </div>1361 <div class="modal-footer justify-content-center">1362 <button1363 type="button"1364 name="btnCloseModal"1365 id="btnCloseModal"1366 class="btn btn-wide"1367 data-dismiss="modal"1368 aria-hidden="true"1369 >1370 No1371 </button>1372 <button1373 type="button"1374 name="btnCloseModal"1375 id="btnCloseModal"1376 class="btn btn--transparent btn-wide"1377 data-dismiss="modal"1378 aria-hidden="true"1379 onClick={() => this.routeCancel()}1380 >1381 Yes1382 </button>1383 </div>1384 </div>1385 </div>1386 </div>1387 </div>1388 </div>1389 <ModalAlert1390 title={this.state.alertModalAlertTitle}1391 visible={this.state.isAlertModalVisible}1392 button={this.state.buttonAlert}1393 isTextOnly={this.state.isTextOnly}1394 >1395 {this.state.alertModalMsg}1396 </ModalAlert>1397 </BlockUi>1398 );1399 }1400}...

Full Screen

Full Screen

view_po.js

Source:view_po.js Github

copy

Full Screen

1dojo.require("dojo.string");2dojo.require('dijit.layout.ContentPane');3dojo.require('openils.PermaCrud');4var pcrud = new openils.PermaCrud();5var PO = null;6var liTable;7var poItemTable;8var poNoteTable;9var invoiceLinkDialogManager;10function AcqPoNoteTable() {11 var self = this;12 this.notesTbody = dojo.byId("acq-po-notes-tbody");13 this.notesRow = this.notesTbody.removeChild(dojo.byId("acq-po-notes-row"));14 dojo.byId("acq-po-notes-back-button").onclick = function() { self.hide(); };15 dojo.byId("acq-po-view-notes").onclick = function() { self.show(); };16 /* widgets' event properties are cased likeThis */17 acqPoCreateNoteSubmit.onClick = function() {18 if (!acqPoCreateNoteText.attr("value")) return;19 /* prep new note */20 var note = new acqpon();21 note.vendor_public(22 Boolean(acqPoCreateNoteVendorPublic.attr('checked'))23 );24 note.value(acqPoCreateNoteText.attr("value"));25 note.purchase_order(PO.id());26 note.isnew(true);27 /* save it */28 self.updatePoNotes(note);29 /* reset fields for next use */30 acqPoCreateNoteText.attr("value", "");31 acqPoCreateNoteVendorPublic.attr("checked", false);32 };33 this.drawPoNote = function(note) {34 if (note.isdeleted())35 return;36 var row = dojo.clone(this.notesRow);37 nodeByName("value", row).innerHTML = note.value();38 if (openils.Util.isTrue(note.vendor_public()))39 nodeByName("vendor_public", row).innerHTML =40 localeStrings.VENDOR_PUBLIC;41 nodeByName("delete", row).onclick = function() {42 note.isdeleted(true);43 self.notesTbody.removeChild(row);44 self.updatePoNotes();45 };46 if (note.edit_time()) {47 nodeByName("edit_time", row).innerHTML =48 dojo.date.locale.format(49 dojo.date.stamp.fromISOString(note.edit_time()),50 {"formatLength": "short"}51 );52 }53 self.notesTbody.appendChild(row);54 };55 this.drawPoNotes = function() {56 /* sort */57 PO.notes(58 PO.notes().sort(59 function(a, b) {60 return (a.edit_time() < b.edit_time()) ? 1 : -1;61 }62 )63 );64 /* remove old renderings of notes */65 dojo.empty(this.notesTbody);66 PO.notes().forEach(function(o) { self.drawPoNote(o); });67 };68 this.updatePoNotesCount = function() {69 dojo.byId("acq-po-view-notes").innerHTML =70 "(" + PO.notes().length + ")";71 };72 this.updatePoNotes = function(newNote) {73 var notes = newNote ?74 [newNote] :75 PO.notes().filter(76 function(o) {77 if (o.ischanged() || o.isnew() || o.isdeleted())78 return o;79 }80 );81 if (notes.length < 1)82 return;83 progressDialog.show();84 fieldmapper.standardRequest(85 ["open-ils.acq", "open-ils.acq.po_note.cud.batch"], {86 "async": true,87 "params": [openils.User.authtoken, notes],88 "onresponse": function(r) {89 var resp = openils.Util.readResponse(r);90 if (resp) {91 progressDialog.update(resp);92 if (!resp.note.isdeleted()) {93 resp.note.isnew(false);94 resp.note.ischanged(false);95 PO.notes().push(resp.note);96 }97 }98 },99 "oncomplete": function() {100 if (!newNote) {101 /* remove the old changed notes */102 var list = [];103 PO.notes(104 PO.notes().filter(105 function(o) {106 return (!(107 o.ischanged() || o.isnew() ||108 o.isdeleted()109 ));110 }111 )112 );113 }114 progressDialog.hide();115 self.updatePoNotesCount();116 self.drawPoNotes();117 }118 }119 );120 };121 this.hide = function() {122 openils.Util.hide("acq-po-notes-div");123 liTable.show("list");124 poItemTable.show();125 };126 this.show = function() {127 liTable.hide();128 poItemTable.hide();129 self.drawPoNotes();130 openils.Util.show("acq-po-notes-div");131 };132}133function updatePoState(po_info) {134 var data = po_info[PO.id()];135 if (data) {136 for (var key in data)137 PO[key](data[key]);138 renderPo();139 }140}141function cancellationUpdater(r) {142 var r = openils.Util.readResponse(r);143 if (r) {144 if (r.po) updatePoState(r.po);145 if (r.li) {146 for (var id in r.li) {147 liTable.liCache[id].state(r.li[id].state);148 liTable.liCache[id].cancel_reason(r.li[id].cancel_reason);149 liTable.updateLiState(liTable.liCache[id]);150 }151 }152 if (r.lid && liTable.copyCache) {153 for (var id in r.lid) {154 if (liTable.copyCache[id]) {155 liTable.copyCache[id].cancel_reason(156 r.lid[id].cancel_reason157 );158 liTable.updateLidState(liTable.copyCache[id]);159 }160 }161 }162 }163}164function makeProviderLink(node, provider) {165 return dojo.create(166 "a", {167 "href": oilsBasePath + "/conify/global/acq/provider/" + provider.id(),168 "innerHTML": provider.name() + " (" + provider.code() + ")",169 },170 node,171 "only"172 );173}174function makePrepayWidget(node, prepay) {175 if (prepay) {176 openils.Util.addCSSClass(node, "oils-acq-po-prepay");177 node.innerHTML = localeStrings.YES;178 } else {179 openils.Util.removeCSSClass(node, "oils-acq-po-prepay");180 node.innerHTML = localeStrings.NO;181 }182}183function makeCancelWidget(node, labelnode) {184 openils.Util.hide("acq-po-choose-cancel-reason");185 if (PO.cancel_reason()) {186 labelnode.innerHTML = localeStrings.CANCEL_REASON;187 node.innerHTML = PO.cancel_reason().description() + " (" +188 PO.cancel_reason().label() + ")";189 } else if (["on-order", "pending"].indexOf(PO.state()) == -1) {190 dojo.destroy(this.oldTip);191 labelnode.innerHTML = "";192 node.innerHTML = "";193 } else {194 dojo.destroy(this.oldTip);195 labelnode.innerHTML = localeStrings.CANCEL;196 node.innerHTML = "";197 if (!acqPoCancelReasonSubmit._prepared) {198 var widget = new openils.widget.AutoFieldWidget({199 "fmField": "cancel_reason",200 "fmClass": "acqpo",201 "parentNode": dojo.byId("acq-po-cancel-reason"),202 "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],203 "forceSync": true204 });205 widget.build(206 function(w, ww) {207 acqPoCancelReasonSubmit.onClick = function() {208 if (w.attr("value")) {209 if (confirm(localeStrings.PO_CANCEL_CONFIRM)) {210 fieldmapper.standardRequest(211 ["open-ils.acq",212 "open-ils.acq.purchase_order.cancel"],213 {214 "params": [215 openils.User.authtoken,216 PO.id(), 217 w.attr("value")218 ],219 "async": true,220 "oncomplete": cancellationUpdater221 }222 );223 }224 }225 };226 acqPoCancelReasonSubmit._prepared = true;227 }228 );229 }230 openils.Util.show("acq-po-choose-cancel-reason", "inline");231 }232}233function prepareInvoiceFeatures() {234 /* show the count of related invoices on the "view invoices" button */235 fieldmapper.standardRequest(236 ["open-ils.acq", "open-ils.acq.invoice.unified_search.atomic"], {237 "params": [238 openils.User.authtoken,239 {"acqpo":[{"id": PO.id()}]},240 null,241 null,242 {"id_list": true}243 ],244 "async": true,245 "oncomplete": function(r) {246 dojo.byId("acq-po-view-invoice-count").innerHTML =247 openils.Util.readResponse(r).length;248 }249 }250 );251 /* view invoices button */252 dijit.byId("acq-po-view-invoice-link").onClick = function() {253 location.href = oilsBasePath + "/acq/search/unified?so=" +254 base64Encode({"jub":[{"purchase_order": PO.id()}]}) +255 "&rt=invoice";256 };257 /* create invoice button */258 dijit.byId("acq-po-create-invoice-link").onClick = function() {259 location.href = oilsBasePath +260 "/acq/invoice/view?create=1&attach_po=" + PO.id();261 };262 openils.Util.show("acq-po-invoice-stuff", "table-cell");263}264function renderPo() {265 dojo.byId("acq-po-view-id").innerHTML = PO.id();266 dojo.byId("acq-po-view-name").innerHTML = PO.name();267 makeProviderLink(268 dojo.byId("acq-po-view-provider"),269 PO.provider()270 );271 dojo.byId("acq-po-view-total-li").innerHTML = PO.lineitem_count();272 dojo.byId("acq-po-view-total-enc").innerHTML = PO.amount_encumbered().toFixed(2);273 dojo.byId("acq-po-view-total-spent").innerHTML = PO.amount_spent().toFixed(2);274 dojo.byId("acq-po-view-state").innerHTML = PO.state(); // TODO i18n275 if(PO.order_date()) {276 openils.Util.show('acq-po-activated-on', 'inline');277 dojo.byId('acq-po-activated-on').innerHTML = 278 dojo.string.substitute(279 localeStrings.PO_ACTIVATED_ON, [280 dojo.date.locale.format(281 dojo.date.stamp.fromISOString(PO.order_date()), 282 {formatLength:'short'}283 )284 ]285 );286 }287 makePrepayWidget(288 dojo.byId("acq-po-view-prepay"),289 openils.Util.isTrue(PO.prepayment_required())290 );291 makeCancelWidget(292 dojo.byId("acq-po-view-cancel-reason"),293 dojo.byId("acq-po-cancel-label")294 );295 // dojo.byId("acq-po-view-notes").innerHTML = PO.notes().length;296 poNoteTable.updatePoNotesCount();297 if (PO.state() == "pending") {298 checkCouldActivatePo();299 if (PO.lineitem_count() > 1)300 openils.Util.show("acq-po-split");301 } else {302 dojo.byId("acq-po-activate-checking").innerHTML = localeStrings.NO;303 }304 // XXX we probably don't *always* need to do this...305 poItemTable.reset();306 PO.po_items().forEach(307 function(po_item) { poItemTable.addItem(po_item); }308 );309 poItemTable.show();310 dojo.attr(311 "acq-po-view-history", "href",312 oilsBasePath + "/acq/po/history/" + PO.id()313 );314 openils.Util.show("acq-po-view-history", "inline");315 prepareInvoiceFeatures();316}317function init() {318 /* set up li table */319 liTable = new AcqLiTable();320 liTable.reset();321 liTable.isPO = poId;322 liTable.poUpdateCallback = updatePoState;323 /* set up po notes table */324 poNoteTable = new AcqPoNoteTable();325 /* retrieve data and populate */326 fieldmapper.standardRequest(327 ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],328 { async: true,329 params: [openils.User.authtoken, poId, {330 "flesh_provider": true,331 "flesh_price_summary": true,332 "flesh_lineitem_count": true,333 "flesh_notes": true,334 "flesh_po_items": true335 }],336 oncomplete: function(r) {337 PO = openils.Util.readResponse(r); /* save PO globally */338 /* po item table */339 poItemTable = new PoItemTable(PO, pcrud);340 renderPo();341 }342 }343 );344 var totalEstimated = 0;345 var zeroLi = true;346 fieldmapper.standardRequest(347 ['open-ils.acq', 'open-ils.acq.lineitem.search'],348 { async: true,349 params: [350 openils.User.authtoken, 351 [{purchase_order:poId}, {"order_by": {"jub": "id ASC"}}], 352 {flesh_attrs:true, flesh_notes:true, flesh_cancel_reason:true, clear_marc:true}353 ],354 onresponse: function(r) {355 zeroLi = false;356 liTable.show('list');357 var li = openils.Util.readResponse(r);358 // TODO: Add po_item's to total estimated amount359 totalEstimated += (Number(li.item_count() || 0) * Number(li.estimated_unit_price() || 0));360 liTable.addLineitem(li);361 },362 oncomplete : function() {363 dojo.byId("acq-po-view-total-estimated").innerHTML = totalEstimated.toFixed(2);364 if (liFocus) liTable.drawCopies(liFocus);365 if(zeroLi) openils.Util.show('acq-po-no-lineitems');366 }367 }368 );369 pcrud.search(370 'acqedim', 371 {purchase_order : poId}, 372 {373 id_list : true,374 oncomplete : function(r) {375 var resp = openils.Util.readResponse(r);376 // TODO: I18n377 if(resp) {378 dojo.byId('acq-po-view-edi-messages').innerHTML = '(' + resp.length + ')';379 dojo.byId('acq-po-view-edi-messages').setAttribute('href', oilsBasePath + '/acq/po/edi_messages/' + poId);380 } else {381 dojo.byId('acq-po-view-edi-messages').innerHTML = '0';382 dojo.byId('acq-po-view-edi-messages').setAttribute('href', '');383 }384 }385 }386 );387}388function checkCouldActivatePo() {389 var d = dojo.byId("acq-po-activate-checking");390 var a = dojo.byId("acq-po-activate-link");391 d.innerHTML = localeStrings.PO_CHECKING;392 var warnings = [];393 var stops = [];394 var other = [];395 fieldmapper.standardRequest(396 ["open-ils.acq", "open-ils.acq.purchase_order.activate.dry_run"], {397 "params": [openils.User.authtoken, PO.id()],398 "async": true,399 "onresponse": function(r) {400 if ((r = openils.Util.readResponse(r, true /* eventOk */))) {401 if (typeof(r.textcode) != "undefined") {402 switch(r.textcode) {403 case "ACQ_FUND_EXCEEDS_STOP_PERCENT":404 stops.push(r);405 break;406 case "ACQ_FUND_EXCEEDS_WARN_PERCENT":407 warnings.push(r);408 break;409 default:410 other.push(r);411 }412 }413 }414 },415 "oncomplete": function() {416 /* XXX in the future, this might be tweaked to display info417 * about more than one stop or warning event from the ML. */418 if (!(warnings.length || stops.length || other.length)) {419 d.innerHTML = localeStrings.PO_COULD_ACTIVATE;420 openils.Util.show(a, "inline");421 } else {422 if (other.length) {423 /* XXX make the textcode part a tooltip one day */424 d.innerHTML = localeStrings.NO + ": " +425 other[0].desc + " (" + other[0].textcode + ")";426 openils.Util.hide(a);427 } else if (stops.length) {428 d.innerHTML =429 dojo.string.substitute(430 localeStrings.PO_STOP_BLOCKS_ACTIVATION, [431 stops[0].payload.fund.code(),432 stops[0].payload.fund.year()433 ]434 );435 openils.Util.hide(a);436 } else {437 PO._warning_hack = true;438 d.innerHTML =439 dojo.string.substitute(440 localeStrings.PO_WARNING_NO_BLOCK_ACTIVATION, [441 warnings[0].payload.fund.code(),442 warnings[0].payload.fund.year()443 ]444 );445 openils.Util.show(a, "inline");446 }447 }448 }449 }450 );451}452function activatePo() {453 if (openils.Util.isTrue(PO.prepayment_required())) {454 if (!confirm(localeStrings.PREPAYMENT_REQUIRED_REMINDER))455 return false;456 }457 if (PO._warning_hack) {458 if (!confirm(localeStrings.PO_FUND_WARNING_CONFIRM))459 return false;460 }461 var want_refresh = false;462 progressDialog.show(true);463 fieldmapper.standardRequest(464 ["open-ils.acq", "open-ils.acq.purchase_order.activate"], {465 "async": true,466 "params": [openils.User.authtoken, PO.id()],467 "onresponse": function(r) {468 want_refresh = Boolean(openils.Util.readResponse(r));469 },470 "oncomplete": function() {471 progressDialog.hide();472 if (want_refresh)473 location.href = location.href;474 }475 }476 );477}478function splitPo() {479 progressDialog.show(true);480 try {481 var list;482 fieldmapper.standardRequest(483 ['open-ils.acq', 'open-ils.acq.purchase_order.split_by_lineitems'],484 { async: true,485 params: [openils.User.authtoken, PO.id()],486 onresponse : function(r) {487 list = openils.Util.readResponse(r);488 },489 oncomplete : function() {490 progressDialog.hide();491 if (list) {492 location.href = oilsBasePath + '/acq/po/search/' +493 list.join(",");494 }495 }496 }497 );498 } catch(E) {499 progressDialog.hide();500 alert(E);501 }502}503function updatePoName() {504 var value = prompt('Enter new purchase order name:', PO.name()); // TODO i18n505 if(!value || value == PO.name()) return;506 PO.name(value);507 pcrud.update(PO, {508 oncomplete : function(r, cudResults) {509 var stat = cudResults[0];510 if(stat)511 dojo.byId('acq-po-view-name').innerHTML = value;512 }513 });514}...

Full Screen

Full Screen

search.js

Source:search.js Github

copy

Full Screen

1dojo.require('dijit.form.Form');2dojo.require('dijit.form.Button');3dojo.require('dijit.form.CheckBox');4dojo.require('dijit.form.FilteringSelect');5dojo.require('dijit.form.NumberTextBox');6dojo.require('dojo.data.ItemFileWriteStore');7dojo.require('dojo.date.locale');8dojo.require('dojo.date.stamp');9dojo.require('openils.User');10dojo.require('openils.Util');11dojo.require('openils.widget.AutoGrid');12dojo.require('openils.widget.AutoFieldWidget');13dojo.require('openils.PermaCrud');14var metaPO;15var _last_fields;16var general_po_search_opts = {"order_by": {"acqpo": "edit_time DESC"}};17function getPOOwner(rowIndex, item) {18 if(!item) return '';19 var data = this.grid.store.getValue(item, 'owner');20 return new openils.User({id:data}).user.usrname();21}22function doSearch(fields) {23 _last_fields = dojo.clone(fields); /* Save for re-use */24 var metapo_view = false;25 /* Remove the metapo_view field from 'fields'... we'll use it later */26 if (fields.metapo_view && fields.metapo_view[0]) {27 metapo_view = true;28 delete fields.metapo_view;29 }30 if (31 !(fields.id && fields.id.constructor.name == 'Array') && 32 isNaN(fields.id)33 ) {34 delete fields.id;35 for(var k in fields) {36 if(fields[k] == '' || fields[k] == null)37 delete fields[k];38 }39 } else {40 // ID search trumps other searches41 fields = {id:fields.id};42 }43 // no search fields44 var some = false;45 for(var k in fields) some = true;46 if(!some) fields.id = {'!=' : null};47 if (metapo_view) {48 openils.Util.hide("holds_po_grid");49 loadMetaPO(fields);50 } else {51 if (metaPO) metaPO.myHide();52 openils.Util.show("holds_po_grid");53 poGrid.resetStore();54 poGrid.loadAll(general_po_search_opts, fields);55 }56}57function loadForm() {58 new openils.widget.AutoFieldWidget({59 fmClass : 'acqpo', 60 fmField : 'provider', 61 parentNode : dojo.byId('po-search-provider-selector'),62 orgLimitPerms : ['VIEW_PURCHASE_ORDER'],63 dijitArgs : {name:'provider', required:false}64 }).build();65 new openils.widget.AutoFieldWidget({66 fmClass : 'acqpo', 67 fmField : 'ordering_agency', 68 parentNode : dojo.byId('po-search-agency-selector'),69 orgLimitPerms : ['VIEW_PURCHASE_ORDER'],70 dijitArgs : {name:'ordering_agency', required:false}71 }).build();72 if (poIds && poIds.length > 0) {73 dijit.byId("metapo_view").attr("checked", true);74 doSearch({"id": poIds, "metapo_view": [true] /* [sic] */});75 } else {76 doSearch({"ordering_agency": openils.User.user.ws_ou()});77 }78}79function loadMetaPO(fields) {80 var pcrud = new openils.PermaCrud();81 var po_list = pcrud.search("acqpo", fields, general_po_search_opts);82 if (!po_list || !po_list.length) {83 alert(localeStrings.NO_PO_RESULTS);84 } else {85 if (!metaPO) {86 metaPO = new AcqLiTable();87 /* We need to know the width (in cells) of the template row for88 * the LI table, and we don't want to hardcode it here. */89 metaPO.n_cells = dojo.query("> td", metaPO.rowTemplate).length;90 metaPO._copy_count_cb = function(liId, count) {91 var poId = this.liCache[liId].purchase_order();92 if (this.copy_counts[poId] == undefined)93 this.copy_counts[poId] = {};94 this.copy_counts[poId][liId] = count;95 this.renderCopyCounts(poId);96 this.renderSummary("copies");97 };98 metaPO.myHide = function() {99 this.hide();100 openils.Util.hide("oils-acq-holds-metapo-summary");101 };102 metaPO.renderSummary = function(part) {103 var self = this;104 /* The idea here will be that if "part" is defined, we'll105 * just update that part of the metaPO summary, otherwise,106 * the whole thing. */107 if (part != undefined) {108 var target = dojo.byId("oils-acq-metapo-summary-" + part);109 switch (part) {110 case "copies":111 target.innerHTML = self.copiesTotal();112 break;113 case "po":114 target.innerHTML = self.working_po_list.length;115 break;116 /* Any numeric fields should be named here. */117 case "amount_encumbered":118 case "amount_spent":119 target.innerHTML = self.numericFieldTotal(part);120 break;121 default:122 /* assume a field on the acqpo's themselves */123 target.innerHTML = self.anyFieldTotal(part);124 break;125 }126 } else {127 openils.Util.show("oils-acq-holds-metapo-summary");128 self.totalable_fields.forEach(129 function(f) { self.renderSummary(f); }130 );131 }132 };133 metaPO.numericFieldTotal = function(field) {134 var self = this;135 var pennies = self.working_po_list.reduce(136 /* working_po_list contains unfleshed acqpo's, so we must137 * find the same PO in the poCache */138 function(p, c) {139 c = self.poCache[c.id()][field]();140 return p + Number(c) * 100;141 }, 0142 );143 return pennies / 100;144 };145 metaPO.anyFieldTotal = function(field) {146 var self = this;147 return self.working_po_list.reduce(148 /* working_po_list contains unfleshed acqpo's, so we must149 * find the same PO in the poCache */150 function(p, c) {151 c = self.poCache[c.id()][field]();152 return p + Number(c);153 }, 0154 );155 };156 metaPO.renderCopyCounts = function(poId) {157 try {158 dojo.query("td#oils-acq-po-heading-" + poId +159 ' span span[attr="copies"]')[0].innerHTML =160 this.copiesByPOId(poId);161 } catch (E) {162 ;163 }164 };165 metaPO.sectionHeadingById = function(id) {166 var headings = dojo.query("#po-heading-" + id, this.tbody);167 if (headings.length != 1) {168 alert(localeStrings.PO_HEADING_ERROR);169 return undefined;170 } else {171 return headings[0];172 }173 };174 metaPO.sectionHeadingByPOId = function(poId) {175 return this.sectionHeadingById(this.sections_by_poid[poId]);176 };177 metaPO.addSection = function(po) {178 var s = this.sections_by_poid[po.id()] = this.sections++;179 this.tbody.appendChild(180 dojo.create("tr", {181 "class": "acq-lit-po-heading", "id": "po-heading-" + s182 })183 );184 return s;185 };186 metaPO.addLineitemToSection = function(li, section) {187 dojo.place(188 this.addLineitem(li, true /* skip_final_placement */),189 this.sectionHeadingById(section),190 "after"191 );192 };193 metaPO.generateActivator = function(id) {194 return function() {195 progressDialog.show(true);196 try {197 fieldmapper.standardRequest(198 ["open-ils.acq",199 "open-ils.acq.purchase_order.activate"], {200 "async": true,201 "params": [openils.User.authtoken, id],202 "oncomplete": function() {203 progressDialog.hide();204 doSearch(_last_fields);205 }206 }207 );208 } catch (E) {209 progressDialog.hide();210 alert(E); /* XXX */211 }212 };213 };214 metaPO.renderHeading = function(poId) {215 var self = this;216 var td = dojo.create("td", {"colspan": self.n_cells});217 td.id = "oils-acq-po-heading-" + poId;218 /* Build our HTML structure from the template... */219 dojo.query("> span", "oils-acq-po-heading-template").forEach(220 function(s) { td.appendChild(s.cloneNode(true)); }221 );222 /* Some fields straight from the PO object... */223 self.po_fields_for_display.forEach(224 function(f) {225 dojo.query('[attr="' + f + '"]', td)[0].innerHTML =226 self.poCache[poId][f]();227 }228 );229 /* The name field needs special treatment: it's a link */230 dojo.attr(231 dojo.query('a[attr="name"]', td)[0],232 "href",233 oilsBasePath + '/acq/po/view/' + poId234 );235 /* Show an "activate" link, or not, based on "state"... */236 var a = dojo.query('a[attr="activator"]', td)[0];237 if (self.poCache[poId].state() == "pending") {238 a.onclick = self.generateActivator(poId);239 openils.Util.show(a, "inline");240 } else {241 openils.Util.hide(a);242 }243 /* Put the new heading cell in place... */244 dojo.place(td, self.sectionHeadingByPOId(poId), "only");245 /* And finally, render copy info (must happen _after_ heading246 * is attached to the DOM tree */247 this.renderCopyCounts(poId);248 };249 metaPO.copiesByPOId = function(poId) {250 if (!this.copy_counts[poId]) return undefined;251 var total = 0;252 for (var liId in this.copy_counts[poId]) {253 total += this.copy_counts[poId][liId];254 }255 return total;256 };257 metaPO.copiesTotal = function() {258 var total = 0;259 for (var poId in this.copy_counts)260 total += this.copiesByPOId(poId);261 return total;262 };263 metaPO.myReset = function() {264 this.isMeta = true;265 this.sections = 0;266 this.sections_by_poid = {};267 this.copy_counts = {};268 this.po_fields_for_display = [269 "name", "lineitem_count", "amount_encumbered",270 "amount_spent", "state"271 ];272 this.totalable_fields = [273 "po", "lineitem_count", "copies",274 "amount_encumbered", "amount_spent"275 ];276 openils.Util.hide("oils-acq-holds-metapo-summary");277 };278 metaPO.populate = function(list) {279 var self = this;280 var done = 0;281 self.working_po_list = [];282 progressDialog.show(true);283 list.forEach(function(po) {284 var sec = self.addSection(po);285 fieldmapper.standardRequest(286 ["open-ils.acq", "open-ils.acq.lineitem.search"], {287 "async": true,288 "params": [289 openils.User.authtoken,290 {"purchase_order": po.id()},291 {"flesh_attrs": true, "flesh_notes": true}292 ],293 "onresponse": function(r) {294 var li = openils.Util.readResponse(r);295 if (li) /* sometimes empty string: disregard */296 self.addLineitemToSection(li, sec);297 },298 "oncomplete": function(r) {299 self.working_po_list.push(po);300 self.renderHeading(po.id());301 self.renderSummary();302 /* This mechanism avoids calling .show() too303 * often or before results are ready, and304 * thus smooths out DOM rendering glitches. */305 if (++done >= list.length) {306 done = -1;307 self.show("list");308 progressDialog.hide();309 }310 }311 }312 );313 });314 /* This mechanism sees to it that we call .show() at least once315 * even if the search result population seems to be timing316 * out or failing. */317 setTimeout(318 function() {319 if (done != -1) {320 self.show("list");321 progressDialog.hide();322 }323 }, 10000 /* 10 seconds: make this configurable? */324 );325 };326 }327 metaPO.reset();328 metaPO.myReset();329 metaPO.populate(po_list);330 }331}...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('webpagetest');2var wpt = new WebPageTest('www.webpagetest.org');3 if (err) return console.log(err);4 console.log(data);5});6{ statusCode: 200,7 { testId: '140729_8W_4e4',8wpt.getTestStatus('140729_8W_4e4', function(err, data) {9 if (err) return console.log(err);10 console.log(data);11});12{ statusCode: 200,13 { statusCode: 200,

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptool = require('wptool');2var po = wptool.po;3var wptool = require('wptool');4var po = wptool.po;5var wptool = require('wptool');6var po = wptool.po;7var wptool = require('wptool');8var po = wptool.po;9var wptool = require('wptool');10var po = wptool.po;11var wptool = require('wptool');12var po = wptool.po;13var wptool = require('wptool');14var po = wptool.po;15var wptool = require('wptool');16var po = wptool.po;17var wptool = require('wptool');18var po = wptool.po;19var wptool = require('wptool');20var po = wptool.po;21var wptool = require('wptool');22var po = wptool.po;23var wptool = require('wptool');24var po = wptool.po;

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptools = require('wptools');2var page = wptools.page('Barack Obama');3page.po(function(err, data) {4 console.log(err || data);5});6var wptools = require('wptools');7var page = wptools.page('Barack Obama');8page.po(function(err, data) {9 console.log(err || data);10});11var wptools = require('wptools');12var page = wptools.page('Barack Obama');13page.po(function(err, data) {14 console.log(err || data);15});16var wptools = require('wptools');17var page = wptools.page('Barack Obama');18page.po(function(err, data) {19 console.log(err || data);20});21var wptools = require('wptools');22var page = wptools.page('Barack Obama');23page.po(function(err, data) {24 console.log(err || data);25});26var wptools = require('wptools');27var page = wptools.page('Barack Obama');28page.po(function(err, data) {29 console.log(err || data);30});31var wptools = require('wptools');32var page = wptools.page('Barack Obama');33page.po(function(err, data) {34 console.log(err || data);35});36var wptools = require('wptools');37var page = wptools.page('Barack Obama');38page.po(function(err, data) {39 console.log(err || data);40});41var wptools = require('wptools');42var page = wptools.page('Barack Obama');43page.po(function(err, data) {44 console.log(err || data);45});

Full Screen

Using AI Code Generation

copy

Full Screen

1$.po('wptextpattern', 'test');2$.po('wptextpattern', 'test');3$.po('wptextpattern', 'test');4$.po('wptextpattern', 'test');5$.po('wptextpattern', 'test');6$.po('wptextpattern', 'test');7$.po('wptextpattern', 'test');8$.po('wptextpattern', 'test');9$.po('wptextpattern', 'test');10$.po('wptextpattern', 'test');11module.exports = function(grunt) {12 grunt.initConfig({13 makepot: {14 target: {15 options: {16 potHeaders: {

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptoolkit = require("wptoolkit");2var wp = wptoolkit.po;3var pathToPoFile = "test.po";4wp.read(pathToPoFile, function(err, data) {5 console.log(data);6});

Full Screen

Using AI Code Generation

copy

Full Screen

1const wptool = require('wptool');2var obj = {name: 'John', age: 21};3var result = wptool.po(obj, 'name');4console.log(result);5const wptool = require('wptool');6var obj = {name: 'John', age: 21};7var result = wptool.pl(obj, 'name');8console.log(result);9const wptool = require('wptool');10var obj = {name: 'John', age: 21};11var result = wptool.po(obj, 'name');12console.log(result);13const wptool = require('wptool');14var obj = {name: 'John', age: 21};15var result = wptool.pl(obj, 'name');16console.log(result);17const wptool = require('wptool');18var obj = {name: 'John', age: 21};19var result = wptool.po(obj, 'name');20console.log(result);21const wptool = require('wptool');22var obj = {name: 'John', age: 21};23var result = wptool.pl(obj, 'name');24console.log(result);25const wptool = require('w

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('webpagetest');2var options = {3};4var wpt = new WebPageTest('www.webpagetest.org', options.key);5 if (err) return console.error(err);6 console.log('Test Results for: ' + data.data.summary);7 console.log('First View (ms): ' + data.data.average.firstView.loadTime);8 console.log('Repeat View (ms): ' + data.data.average.repeatView.loadTime);9});

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run wpt automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful