[javascript] FulfillmentDialog.vue
Viewer
*** This page was generated with the meta tag "noindex, nofollow". This happened because you selected this option before saving or the system detected it as spam. This means that this page will never get into the search engines and the search bot will not crawl it. There is nothing to worry about, you can still share it with anyone.
- <template>
- <v-layout row>
- <v-flex grow>
- <!-- VOUCHER POPUP ERROR -->
- <v-dialog max-width="450px" v-if="voucherErrorDialogShow" v-model="voucherErrorDialogShow">
- <v-layout justify-center align-center>
- <v-card width="100%">
- <v-card-title><span class="subheading font-weight-bold">Voucher Details</span></v-card-title>
- <v-card-text>
- Voucher <span style="color:red;">Invalid</span> because:
- <span v-html="voucherErrorDialogMessage">{{voucherErrorDialogMessage}}</span>
- </v-card-text>
- <v-card-actions>
- <v-spacer></v-spacer>
- <v-btn flat @click="closeVoucherErrorMessageDialog()" style="color: red;">
- Close
- </v-btn>
- </v-card-actions>
- </v-card>
- </v-layout>
- </v-dialog>
- <!-- /VOUCHER POPUP ERROR -->
- <v-dialog v-model="dialogFulfill" persistent fullscreen hide-overlay transition="dialog-bottom-transition">
- <v-card>
- <v-toolbar dark color="primary">
- <v-btn icon dark @click="closeFulfillment">
- <v-icon color="#FFFFFF">close</v-icon>
- </v-btn>
- <v-toolbar-title>ADJUST FULFILLMENT</v-toolbar-title>
- <v-spacer></v-spacer>
- <v-btn color="amber darken-2" depressed @click="submitFulfillment">Confirm Fulfillment</v-btn>
- </v-toolbar>
- <v-container grid-list-md fluid>
- <v-layout row wrap>
- <!-- LEFT -->
- <v-flex xs12 sm9>
- <v-layout column>
- <v-flex class="mb-3">
- <h1 class="title font-weight-bold">{{ mainTableTitle }}</h1>
- </v-flex>
- <v-flex>
- <!-- BULK MODE -->
- <v-layout column>
- <v-flex class="mb-2">
- <v-card>
- <v-divider></v-divider>
- <v-container fluid class="mt-0 mx-0 pa-0">
- <v-data-table
- :loading="loadingSOList"
- :headers="headersFulfillment"
- :items="fulfilled"
- :hide-actions="true"
- class="elevation-1"
- >
- <template v-slot:progress>
- <v-layout row mt-2>
- <v-flex>
- <v-progress-circular indeterminate color="primary"></v-progress-circular>
- </v-flex>
- </v-layout>
- </template>
- <template v-slot:items="props">
- <tr :style="props.item.payment_type !== 'cash' && type !== 'st' ? 'background-color: #deffdf' : ''">
- <td style="white-space: nowrap">
- <div class="d-flex justify-space-between align-center">
- <v-icon @click="removeSalesOrder(props.item)" color="error" left>remove_circle</v-icon>
- <span
- class="text-uppercase"
- :class="{
- 'green--text' : props.item.success,
- 'red--text' : !props.item.success
- }"
- >
- {{ props.item.success ? 'Success' : 'Failed' }}
- </span>
- </div>
- </td>
- <td>{{ props.item.id }}</td>
- <td>{{ props.item.store_name }}</td>
- <td>
- <table class="products-table">
- <tr>
- <th>Product Name</th>
- <th>Unit</th>
- <th align="right">Fulfilled</th>
- <th align="right">Request</th>
- </tr>
- <template v-for="item in props.item.products">
- <tr v-for="unit in item.units" :key="`${item.product_id}-${unit.unit_name}`">
- <td>{{ item.name || '-' }}</td>
- <td>{{ unit.unit_name ? _(unit.unit_name).capitalize() : '-' }}</td>
- <td>
- <v-icon color="red"
- v-if="isFulfilledStatusValid(unit.fulfilled_quantity,unit.quantity, props.item.id, unit.product_id) === false">warning
- </v-icon>
- {{ (unit.fulfilled_quantity || 0) | thousandSeparator }}
- </td>
- <td>{{ (unit.quantity || 0) | thousandSeparator }}</td>
- </tr>
- </template>
- </table>
- </td>
- <td>
- <v-btn
- v-if="type === 'st' || props.item.payment_type === 'cash'"
- small
- depressed
- color="warning"
- style="height: 28px;"
- @click="editFulfill(props.item)"
- >Fulfill
- </v-btn>
- </td>
- </tr>
- <tr v-if="orders[props.item.id] && orders[props.item.id].voucher && isVoucherDetailLoaded === true" style="size:14px; border: 0px solid black;" class="text-xs-right">
- <td colspan="6">
- <template v-if="isVoucherStatusValid(props.item.products, props.item.id) === true">
- <span class="success--text body-2">
- Voucher Valid
- </span>
- </template>
- <template v-else>
- <a @click="showVoucherErrorDialog(props.item.products, props.item.id)" id="voucher-status-tooltip" class="error--text body-2" title="See details">
- <v-icon color="red" depressed size="16px">info</v-icon>
- Voucher Invalid
- </a>
- </template>
- </td>
- </tr>
- </template>
- </v-data-table>
- </v-container>
- </v-card>
- </v-flex>
- </v-layout>
- </v-flex>
- <!-- SALES EXCHANGES -->
- <v-flex v-if="fulfilledSalesExchange.length > 0">
- <!-- BULK MODE -->
- <v-layout column>
- <v-flex class="mb-2">
- <v-card>
- <v-card-title>
- <span class="subheading black--text">SALES EXCHANGES</span>
- </v-card-title>
- <v-divider></v-divider>
- <v-container fluid class="mt-0 mx-0 pa-0">
- <v-data-table
- :loading="loadingSOList"
- :headers="headersFulfillment"
- :items="fulfilledSalesExchange"
- :hide-actions="true"
- class="elevation-1"
- >
- <template v-slot:progress>
- <v-layout row mt-2>
- <v-flex>
- <v-progress-circular indeterminate color="primary"></v-progress-circular>
- </v-flex>
- </v-layout>
- </template>
- <template v-slot:items="props">
- <td>
- <strong
- class="text-uppercase"
- :class="{
- 'green--text' : props.item.success,
- 'red--text' : !props.item.success
- }"
- >
- {{ props.item.success ? 'Success' : 'Failed' }}
- </strong>
- </td>
- <td>{{ props.item.id }}</td>
- <td>{{ props.item.store_name }}</td>
- <td>
- <table class="products-table">
- <tr>
- <th>Product Name</th>
- <th>Unit</th>
- <th align="right">Fulfilled</th>
- <th align="right">Request</th>
- </tr>
- <template v-for="item in props.item.products">
- <tr v-for="unit in item.units" :key="`${unit.product_id}${unit.unit_name}`">
- <td>{{ item.name || '-' }}</td>
- <td>{{ unit.unit_name ? _(unit.unit_name).capitalize() : '-' }}</td>
- <td>
- <v-icon color="red"
- v-if="isFulfilledStatusValid(unit.fulfilled_quantity,unit.quantity, props.item.id, unit.product_id) === false">warning
- </v-icon>
- {{ (unit.fulfilled_quantity || 0) | thousandSeparator }}
- </td>
- <td>{{ (unit.quantity || 0) | thousandSeparator }}</td>
- </tr>
- </template>
- </table>
- </td>
- <td>
- <v-btn
- depressed
- color="warning"
- @click="editFulfill(props.item)"
- >Fulfill
- </v-btn>
- </td>
- </template>
- </v-data-table>
- </v-container>
- </v-card>
- </v-flex>
- </v-layout>
- </v-flex>
- </v-layout>
- </v-flex>
- <!-- RIGHT -->
- <v-flex xs12 sm3>
- <v-layout column>
- <v-flex class="mb-3">
- <h2 class="title font-weight-bold">Total Product</h2>
- </v-flex>
- <v-flex>
- <v-data-table
- :headers="headersTotalProduct"
- :items="totalFulfilledProducts"
- hide-actions
- class="elevation-1"
- >
- <template v-slot:items="props">
- <td style="padding-right: 0px">{{ props.item.name || '-' }}</td>
- <td colspan="2" style="padding-right: 0px">
- <table class="summary-products-table">
- <tr v-for="(unit, indexUnit) in props.item.units" :key="indexUnit">
- <td>{{ unit.unit_name || '-' }}</td>
- <td>{{ (unit.fulfilled_quantity || 0) | thousandSeparator }}</td>
- </tr>
- </table>
- </td>
- </template>
- </v-data-table>
- </v-flex>
- </v-layout>
- </v-flex>
- </v-layout>
- </v-container>
- </v-card>
- </v-dialog>
- <!-- DIALOG MANUAL FULFILL -->
- <v-dialog v-model="editFulfillment.dialog" v-if="editFulfillment.dialog" max-width="700px">
- <v-layout column>
- <v-flex>
- <v-card>
- <v-card-title>
- <span class="title black--text font-weight-bold">{{ editFulfillment.selectedFulfill.id }} - {{ editFulfillment.selectedFulfill.store_name }}</span>
- </v-card-title>
- <v-divider></v-divider>
- <v-container fluid class="ma-0 pa-0">
- <v-data-table
- :headers="headersEditFulfill"
- :items="editFulfillmentProducts"
- :hide-actions="true"
- class="elevation-1"
- >
- <template slot="items" slot-scope="props">
- <tr v-for="unit in props.item.units" :key="`${props.item.product_id}-${unit.unit_name}`">
- <td>{{ props.item.name || '-' }}</td>
- <td class="text-capitalize">{{ unit.unit_name || '-' }}</td>
- <td>
- <v-layout row wrap align-center>
- <v-flex xs3 sm3 md3>
- <v-currency-field
- :decimal-length="0"
- :value-as-integer="true"
- :value="unit.fulfilled_quantity"
- type="number"
- v-model.number="unit.fulfilled_quantity"
- :min="0"
- single-line
- v-validate="{ 'required': true, 'numeric': true, 'between': { 'min': 0, 'max': Math.min(tempTotalStockAvailable[props.item.product_id].unit_stock_available_obj[unit.unit_name].stock_available + unit.fulfilled_quantity, parseInt(unit.quantity)) } }"
- :error-messages="errors.first('edit-fulfillment.fulfill-' + props.item.product_id + unit.unit_name)"
- data-vv-scope="edit-fulfillment"
- data-vv-as="Quantity"
- :data-vv-name="'edit-fulfillment.fulfill-' + props.item.product_id + unit.unit_name"
- >
- </v-currency-field>
- </v-flex>
- <v-flex xs3 sm3 md3 class="subheading">
- / {{ unit.quantity | thousandSeparator }}
- </v-flex>
- </v-layout>
- </td>
- <td>{{ tempTotalStockAvailable[props.item.product_id].unit_stock_available_obj[unit.unit_name].stock_available | thousandSeparator }}</td>
- </tr>
- </template>
- </v-data-table>
- </v-container>
- <v-card-actions>
- <v-spacer></v-spacer>
- <v-btn flat color="dark" @click="editFulfillment.dialog = false">Cancel</v-btn>
- <v-btn flat color="primary" @click="submitEditFulfillment">Confirm</v-btn>
- </v-card-actions>
- </v-card>
- </v-flex>
- </v-layout>
- </v-dialog>
- <!-- SNACKBAR !-->
- <v-snackbar top color="red" :timeout="7000" v-model="snackbar">
- <span style="color:white;">{{ snackbarText }}</span>
- </v-snackbar>
- <!-- /Dialog Fullfill V2 Putaway -->
- <v-dialog
- v-model="confirmDialog"
- persistent
- max-width="400px"
- >
- <v-form v-model="updateFormValid" ref="confirmUpdateToOnLoading">
- <v-card>
- <v-card-title class="title font-weight-bold">Process SO</v-card-title>
- <v-divider/>
- <v-card-text v-if="failedOrders.length > 0">
- <v-layout row wrap>
- <v-flex xs12 md12 sm12 lg12 class="pb-2" v-if="failedOrders.length > 0">
- <span style="font-weight:bold; font-size:16px;">
- <v-icon color="warning" class="pr-1">warning</v-icon>
- Warning there
- <span v-if="failedOrders.length > 0 && failedOrders.length <= 1">is </span>
- <span v-else>are</span>
- <span style="color:red;">FAILED</span>
- <span>Order</span><span v-if="failedOrders.length > 1">s</span>
- </span>
- </v-flex>
- <v-flex xs12 md12 sm12 lg12 v-if="failedOrders.length > 0">
- <v-layout row wrap v-for="(failedOrder, index) in failedOrders" :key="index">
- <v-flex lg12 md12 sm12 xs12 class="pb-1">
- <span v-if="!failedOrder.success">{{failedOrder.id}} - <span style="color:red; font-weight:bold;">FAILED</span> </span>
- </v-flex>
- </v-layout>
- </v-flex>
- </v-layout>
- </v-card-text>
- <v-divider v-if="failedOrders.length > 0"></v-divider>
- <v-card-text>
- Are you sure to process {{ totalSuccessOrders }} selected SO?
- </v-card-text>
- <v-divider></v-divider>
- <v-card-text>
- <v-layout column>
- <v-flex>
- <label for="routing">Routing Process<span class="red--text">*</span></label>
- </v-flex>
- <v-flex>
- <v-radio-group v-model="routingMethod" color="primary" row>
- <v-radio label="Manual" value="manual" color="primary"></v-radio>
- <v-radio label="Rudolve" value="rudolve" color="primary" :disabled="type === 'st'"></v-radio>
- </v-radio-group>
- </v-flex>
- <template v-if="routingMethod === 'manual'">
- <v-flex>
- <label for="delivery_date">Delivery Date</label>
- </v-flex>
- <v-flex>
- <v-layout row wrap>
- <v-flex xs6>
- <databoard-date-picker
- v-model="deliveryDate.selected"
- :min="deliveryDate.min"
- :outline="false"
- required
- solo
- class="solo-outline"
- :rules="[ x => !!x || 'Please choose delivery date' ]"
- ></databoard-date-picker>
- </v-flex>
- </v-layout>
- </v-flex>
- </template>
- </v-layout>
- </v-card-text>
- <v-divider></v-divider>
- <v-divider/>
- <v-card-actions>
- <v-spacer></v-spacer>
- <v-btn color="grey darken-2" flat @click="closeConfirmDialog()">Cancel</v-btn>
- <v-btn flat color="green darken-1" :disabled="updateFormValid === false" :loading="fullscreenLoading" @click="confirmFulfillment" class="white--text">Yes</v-btn>
- </v-card-actions>
- </v-card>
- </v-form>
- </v-dialog>
- <!-- LOADING -->
- <v-dialog v-if="fullscreenLoading" v-model="fullscreenLoading" fullscreen full-width>
- <v-container fluid fill-height style="background-color: rgba(255, 255, 255, 0.5);">
- <v-layout justify-center align-center>
- <v-progress-circular
- indeterminate
- color="primary">
- </v-progress-circular>
- </v-layout>
- </v-container>
- </v-dialog>
- <!-- END LOADING -->
- </v-flex>
- </v-layout>
- </template>
- <script>
- import _ from 'underscore'
- import moment from 'moment'
- import DataboardDatePicker from '@/components/FieldComponents/DataboardDatePicker'
- import { validateDistributionCentreSalesOrders } from './scripts/fulfillment'
- const FulfillmentFunctions = require('./scripts/fulfillment')
- export default {
- props: {
- orders: {type: Object, required: false},
- value: {type: Boolean, required: true},
- type: {type: String, required: true}
- },
- components: {
- DataboardDatePicker
- },
- data () {
- return {
- isVoucherDetailLoaded: false,
- invalidReasons: {},
- ruleProductDetails: {},
- totalFulfilledPrice: {},
- totalFulfilledPricePercent: {},
- totalFulfilledQtyPercent: {},
- checkerList: [],
- selectedChecker: '',
- updateFormValid: false,
- snackbar: false,
- snackbarText: '',
- fullscreenLoading: true,
- loadingSOList: false,
- loadingPickingList: false,
- headersFulfillment: [
- { text: 'STATUS', value: 'status', sortable: false },
- { text: 'ORDER ID ', value: 'value.id', sortable: false },
- { text: 'MERCHANT', value: 'value.merchant.store_name', sortable: false },
- { text: 'PRODUCTS', value: 'value.products_fulfilled', sortable: false },
- { text: '', value: 'action', sortable: false }
- ],
- headersTotalProduct: [
- { text: 'PRODUCTS', value: 'name', width: '50%', sortable: false },
- { text: 'UNIT', value: 'unit_name', sortable: false },
- { text: '', value: 'quantity', sortable: false }
- ],
- headersEditFulfill: [
- { text: 'PRODUCTS', value: 'name', sortable: false },
- { text: 'UNIT', value: 'unit_name', sortable: false },
- { text: 'FULFILLED/REQUEST', value: 'fulfilled_request', sortable: false },
- { text: 'STOCK AVAILABLE', value: 'available', sortable: false }
- ],
- fulfillmentResult: {},
- distributionCentre: '',
- editFulfillment: {
- dialog: false,
- selectedFulfill: {},
- originalSelectedFulfill: {}
- },
- voucherErrorDialogShow: false,
- voucherErrorDialogMessage: '',
- editPickingLocationData: {
- dialog: false,
- selectedProduct: {},
- alert: {
- status: false,
- message: '',
- type: ''
- }
- },
- // Confirm dialog
- confirmDialog: false,
- deliveryDate: {
- selected: moment().format('YYYY-MM-DD'),
- min: moment().format('YYYY-MM-DD')
- },
- productDetails: [],
- // Default
- routingMethod: 'manual'
- }
- },
- async beforeMount () {
- // this.isVoucherDetailLoaded = false
- // if (this.type === 'so') {
- // await this.queryProductDetails()
- // await this.queryVoucherDetails()
- // }
- // this.isVoucherDetailLoaded = true
- },
- created () {
- this.initialFulfillment()
- this.getDistributionCentre()
- // this.getCheckers()
- },
- computed: {
- mainTableTitle () {
- return this.type === 'st' ? 'Stock Transfer' : 'Sales Order'
- },
- failedOrders () {
- if (!this.fulfillmentResult.fulfilled) return []
- let salesOrders = _.toArray(this.fulfillmentResult.fulfilled.sales_orders)
- let salesExchanges = _.toArray(this.fulfillmentResult.fulfilled.sales_exchanges)
- let stockTransfers = _.toArray(this.fulfillmentResult.fulfilled.stock_transfers)
- let failedArr = []
- salesOrders.forEach(salesOrder => {
- if (!salesOrder.success) failedArr.push({id: salesOrder.id, success: salesOrder.success})
- })
- salesExchanges.forEach(salesExchange => {
- if (!salesExchange.success && !salesExchange.showAsSalesExchange) failedArr.push({id: salesExchange.id, success: salesExchange.success})
- })
- stockTransfers.forEach(stockTransfer => {
- if (!stockTransfer.success) failedArr.push({id: stockTransfer.id, success: stockTransfer.success})
- })
- return failedArr
- },
- successOrders () {
- if (!this.fulfillmentResult.fulfilled) return []
- let successFulfillment = {}
- if (this.type === 'so') {
- let salesOrders = { ...this.fulfillmentResult.fulfilled.sales_orders }
- _.keys(salesOrders).forEach(salesOrderId => {
- if (!salesOrders[salesOrderId].success) delete (salesOrders[salesOrderId])
- })
- if (!_.isEmpty(salesOrders)) successFulfillment.salesOrders = salesOrders
- let salesExchanges = { ...this.fulfillmentResult.fulfilled.sales_exchanges }
- _.keys(salesExchanges).forEach(salesExchangeId => {
- if (!salesExchanges[salesExchangeId].success) delete (salesExchanges[salesExchangeId])
- })
- if (!_.isEmpty(salesExchanges)) successFulfillment.salesExchanges = salesExchanges
- } else if (this.type === 'st') {
- let stockTransfers = { ...this.fulfillmentResult.fulfilled.stock_transfers }
- _.keys(stockTransfers).forEach(stockTransferId => {
- if (!stockTransfers[stockTransferId].success) delete (stockTransfers[stockTransferId])
- })
- if (!_.isEmpty(stockTransfers)) successFulfillment.stockTransfers = stockTransfers
- }
- return successFulfillment
- },
- totalSuccessOrders () {
- if (_.isEmpty(this.successOrders)) return 0
- if (this.type === 'so') {
- let totalSO = 0
- let totalSE = 0
- if (this.successOrders.salesOrders) totalSO += Object.keys(this.successOrders.salesOrders).length
- if (this.successOrders.salesExchanges) totalSE += Object.keys(this.successOrders.salesExchanges).length
- return totalSO + totalSE
- } else if (this.type === 'st') {
- let totalST = 0
- if (this.successOrders.stockTransfers) totalST += Object.keys(this.successOrders.stockTransfers).length
- return totalST
- }
- },
- totalFulfilledProducts () {
- const summary = {}
- for (const key in this.fulfillmentResult.fulfilled) {
- // `key` is either `sales_orders`, `sales_exchanges`, or `stock_transfers`
- const fulfilled = JSON.parse(JSON.stringify(this.fulfillmentResult.fulfilled[key]))
- Object.keys(fulfilled).forEach(id => {
- Object.keys(fulfilled[id].products).forEach(productId => {
- if (!summary[productId]) {
- summary[productId] = {
- name: fulfilled[id].products[productId].name,
- id: productId,
- units: JSON.parse(JSON.stringify(fulfilled[id].products[productId].units))
- }
- } else {
- summary[productId].units.forEach(existingUnit => {
- fulfilled[id].products[productId].units.forEach(unit => {
- if (unit.unit_name === existingUnit.unit_name) {
- existingUnit.fulfilled_quantity += unit.fulfilled_quantity
- }
- })
- })
- }
- })
- })
- }
- return Object.values(summary)
- .map((summaryItem) => ({
- name: summaryItem.name,
- id: summaryItem.id,
- units: summaryItem.units.filter(unit => unit.fulfilled_quantity > 0)
- }))
- .filter(summaryItem => summaryItem.units.length > 0)
- .sort((a, b) => {
- if (a.name < b.name) return -1
- else if (a.name > b.name) return 1
- else return 0
- })
- },
- // Main dialog
- dialogFulfill: {
- get () {
- return this.value
- },
- set (value) {
- this.$emit('input', value)
- }
- },
- // To Array Fulfillment Result
- fulfilled () {
- if (!this.fulfillmentResult.fulfilled) return []
- else {
- if (this.type === 'so') {
- if (this.fulfillmentResult.fulfilled.sales_orders) {
- let orders = _.toArray(this.fulfillmentResult.fulfilled.sales_orders)
- orders.forEach(order => {
- Object.keys(order.products).forEach(id => {
- order.products[id].units.forEach(unit => {
- unit.product_id = id
- })
- })
- })
- // add sorting by payment type
- // orders = _(orders).chain().sortBy(order => { return order.payment_type !== 'cash' }).value().reverse()
- return orders
- } else return []
- } else if (this.type === 'st') return this.fulfillmentResult.fulfilled.stock_transfers ? _.toArray(this.fulfillmentResult.fulfilled.stock_transfers) : []
- }
- },
- fulfilledSalesExchange () {
- if (!this.fulfillmentResult.fulfilled) return []
- else return this.fulfillmentResult ? _.toArray(this.fulfillmentResult.fulfilled.sales_exchanges) : []
- },
- pickingLocations () {
- return this.fulfillmentResult.locations ? _.toArray(this.fulfillmentResult.locations) : []
- },
- // Edit fulfillment
- editFulfillmentProducts () {
- return this.editFulfillment.selectedFulfill.products ? _.toArray(this.editFulfillment.selectedFulfill.products) : []
- },
- tempTotalStockAvailable () {
- let products = JSON.parse(JSON.stringify(this.products))
- Object.keys(products).forEach(productId => {
- products[productId].unit_stock_available_obj = {}
- products[productId].unit_stock_available.forEach(unit => {
- products[productId].unit_stock_available_obj[unit.name] = {...unit}
- })
- })
- if (this.editFulfillment.selectedFulfill) {
- Object.keys(products).forEach(productId => {
- Object.keys(this.editFulfillment.selectedFulfill.products).forEach(editedProductId => {
- if (productId === editedProductId) {
- let totalConvertedQty = 0
- this.editFulfillment.selectedFulfill.products[productId].units.forEach(unit => {
- let convertedQty = unit.fulfilled_quantity * products[productId].unit_stock_available_obj[unit.unit_name].unit_quantity
- totalConvertedQty += convertedQty
- products[productId].total_stock_available -= convertedQty
- })
- this.editFulfillment.selectedFulfill.products[productId].qty = totalConvertedQty
- }
- })
- Object.keys(this.editFulfillment.originalSelectedFulfill.products).forEach(editedProductId => {
- if (productId === editedProductId) {
- this.editFulfillment.originalSelectedFulfill.products[productId].units.forEach(unit => {
- let convertedQty = unit.fulfilled_quantity * products[productId].unit_stock_available_obj[unit.unit_name].unit_quantity
- products[productId].total_stock_available += convertedQty
- })
- }
- })
- Object.values(products[productId].unit_stock_available_obj).forEach(unit => {
- unit.stock_available = Math.floor(products[productId].total_stock_available / unit.unit_quantity)
- })
- })
- }
- return products
- },
- // Utility
- products () {
- return this.fulfillmentResult.products
- }
- },
- methods: {
- originalRequestedQuantity (productId, unitName) {
- if (productId !== undefined && unitName !== undefined) {
- let quantity = this.editFulfillment.originalSelectedFulfill.products[productId].units.filter(unit => unit.unit_name === unitName)
- if (quantity.length > 0) return quantity[0].fulfilled_quantity
- else return 0
- } else {
- return 0
- }
- },
- unitStockAvailable (productId, unitName) {
- if (productId !== undefined && unitName !== undefined) {
- let quantity = this.products[productId].unit_stock_available.filter(unit => unit.name === unitName)
- if (quantity.length > 0) return quantity[0].stock_available
- else return 0
- } else {
- return 0
- }
- },
- async queryProductDetails () {
- const salesOrderProductIds = []
- for (const orderKey in this.orders) {
- const salesOrder = this.orders[orderKey]
- const product = salesOrder.products_requested
- const productIds = Object.keys(product)
- salesOrderProductIds.push(...productIds)
- }
- const filteredDuplicatedProductIds = salesOrderProductIds.filter((item, pos, self) => self.indexOf(item) === pos)
- const payload = {
- products: filteredDuplicatedProductIds
- }
- const queryResult = await this.$store.dispatch('purchases/fetchProductsDetails', payload)
- this.productDetails = queryResult
- },
- async queryVoucherDetails () {
- for (const orderKey in this.orders) {
- if (!this.orders[orderKey] || !this.orders[orderKey].voucher) continue
- if (this.orders.rules) continue
- const payload = {
- salesOrder: this.orders[orderKey]
- }
- const salesOrder = await this.$store.dispatch('purchases/fetchPOVoucherDetails', payload)
- this.orders[orderKey].voucher = salesOrder.voucher
- }
- },
- showVoucherErrorDialog (products, salesOrderId) {
- if (typeof products === 'object') {
- this.voucherErrorDialogMessage = 'Internal server error'
- }
- const invalidReasons = this.invalidReasons[salesOrderId]
- if (invalidReasons.length !== 0) {
- this.voucherErrorDialogMessage = `<ol>${invalidReasons.map((value, index) => `<li>${value}</li>`).join('')}</ol>`
- }
- this.voucherErrorDialogShow = true
- },
- closeConfirmDialog () {
- this.confirmDialog = false
- this.selectedChecker = ''
- this.$refs.confirmUpdateToOnLoading.resetValidation()
- },
- // AUTO FULFILLMENT
- async initialFulfillment () {
- this.isVoucherDetailLoaded = false
- this.loadingSOList = true
- this.loadingPickingList = true
- this.fullscreenLoading = true
- let params
- if (this.type === 'so') {
- params = { sales_orders: this.orders }
- } else {
- let stockTransfers = (await this.$store.dispatch('stockTransfer/getStockTransfer', _.keys(this.orders)[0])).data
- params = { stock_transfers: { [`${stockTransfers.id}`]: stockTransfers } }
- }
- if (this.type === 'so') {
- await this.queryProductDetails()
- await this.queryVoucherDetails()
- }
- await this.$store.dispatch('sales/autoFulfill', params).then(result => {
- let data = result.data
- Object.keys(data.products).forEach(productId => {
- data.products[productId]['unit_stock_available'] = data.products[productId].unit_conversions.map(unit => ({
- ...unit,
- stock_available: Number(0)
- }))
- let TOTAL_STOCK_LEFT = data.products[productId].total_stock_available
- data.products[productId].unit_stock_available.forEach(unit => {
- unit.stock_available = Math.floor(TOTAL_STOCK_LEFT / unit.unit_quantity)
- })
- })
- this.$set(this, 'fulfillmentResult', data)
- this.loadingSOList = false
- this.loadingPickingList = false
- this.fullscreenLoading = false
- this.isVoucherDetailLoaded = true
- })
- },
- callSnackbar (message) {
- this.snackbar = true
- this.snackbarText = message
- },
- getDistributionCentre () {
- if (this.type === 'st') {
- this.distributionCentre = this.orders[Object.keys(this.orders)[0]].dc_sender.formatted_name
- } else {
- // assumed 'so'
- if (validateDistributionCentreSalesOrders(this.orders)) {
- this.distributionCentre = this.orders[_.keys(this.orders)[0]].merchant.distribution_centre || 'bsd_taman_tekno'
- }
- }
- },
- // EDIT FULFILLMENT
- editFulfill (item) {
- this.$set(this.editFulfillment, 'originalSelectedFulfill', item)
- this.$set(this.editFulfillment, 'selectedFulfill', JSON.parse(JSON.stringify(item)))
- this.checkSalesExchangeRelation()
- this.$set(this.editFulfillment, 'dialog', true)
- },
- checkSalesExchangeRelation () {
- // check for SE that have relation to SO
- if (this.editFulfillment.originalSelectedFulfill.sales_order_id && this.type === 'so') {
- let salesOrders = this.fulfillmentResult.fulfilled.sales_orders
- Object.keys(salesOrders).forEach(salesOrderId => {
- let salesExchange = this.editFulfillment.originalSelectedFulfill
- if (salesExchange.sales_order_id === salesOrderId && !salesOrders[salesOrderId].success) {
- this.callSnackbar('SO ' + salesExchange.sales_order_id + ' must be SUCCESS to fulfill this SE')
- this.$set(this.editFulfillment, 'dialog', false)
- }
- })
- }
- },
- async submitEditFulfillment () {
- // Check negative fulfillment and overload fulfillment
- let valid = await this.$validator.validateAll('edit-fulfillment')
- if (!valid) return
- // Update success status
- let id = this.editFulfillment.originalSelectedFulfill.id
- let success = false
- _.keys(this.editFulfillment.selectedFulfill.products).forEach(productId => {
- this.editFulfillment.selectedFulfill.products[productId].units.forEach(unit => {
- if (unit.fulfilled_quantity > 0) success = true
- })
- })
- this.editFulfillment.selectedFulfill.success = success
- if (this.type === 'so') { // condition for sales exchange
- if (!this.editFulfillment.originalSelectedFulfill.sales_order_id) this.fulfillmentResult.fulfilled.sales_orders[id] = this.editFulfillment.selectedFulfill
- else this.fulfillmentResult.fulfilled.sales_exchanges[id] = this.editFulfillment.selectedFulfill
- } else if (this.type === 'st') {
- this.fulfillmentResult.fulfilled.stock_transfers[id] = this.editFulfillment.selectedFulfill
- }
- FulfillmentFunctions.returnStockToProducts(
- this.fulfillmentResult.products,
- this.editFulfillment.originalSelectedFulfill.products,
- this.editFulfillment.selectedFulfill.products
- )
- // check for SO that have relation to SE
- if (this.type === 'so') {
- let salesExchanges = this.fulfillmentResult.fulfilled.sales_exchanges
- Object.keys(salesExchanges).forEach(salesExchangeId => {
- if (id === salesExchanges[salesExchangeId].sales_order_id) {
- this.changeStatusSalesExchange(salesExchanges[salesExchangeId], success)
- }
- })
- }
- this.$set(this.editFulfillment, 'dialog', false)
- await this.recalculateTotalPrice()
- },
- changeStatusSalesExchange (item, success) {
- const id = item.id
- const originalSelectedFulfill = item
- const selectedFulfill = JSON.parse(JSON.stringify(item))
- _.keys(selectedFulfill.products).forEach(productId => {
- if (!success) {
- selectedFulfill.success = success
- selectedFulfill.products[productId].qty = 0
- for (const unit of selectedFulfill.products[productId].units) {
- unit.fulfilled_quantity = 0
- }
- }
- })
- this.fulfillmentResult.fulfilled.sales_exchanges[id] = selectedFulfill
- FulfillmentFunctions.returnStockToProducts(
- this.fulfillmentResult.products,
- originalSelectedFulfill.products,
- selectedFulfill.products
- )
- },
- updateSalesOrder (id) {
- this.$set(
- this.fulfillmentResult.updated_sales_orders,
- id,
- FulfillmentFunctions.updateSalesOrderInFulfillment(
- this.orders[id],
- this.fulfillmentResult.fulfilled.sales_orders[id].products
- ).sales_order
- )
- },
- // REMOVE SALES ORDER
- removeSalesOrder (item) {
- const id = item.id
- if (this.type === 'so') {
- FulfillmentFunctions.returnStockToProducts(this.fulfillmentResult.products, this.fulfillmentResult.fulfilled.sales_orders[id].products)
- this.$delete(this.fulfillmentResult.fulfilled.sales_orders, id)
- if (this.fulfillmentResult.fulfilled.sales_exchanges) {
- let salesExchanges = this.fulfillmentResult.fulfilled.sales_exchanges
- Object.keys(salesExchanges).forEach(salesExchangeId => {
- if (id === salesExchanges[salesExchangeId].sales_order_id) {
- FulfillmentFunctions.returnStockToProducts(this.fulfillmentResult.products, salesExchanges[salesExchangeId].products)
- this.$delete(salesExchanges, salesExchangeId)
- }
- })
- }
- } else if (this.type === 'st') {
- FulfillmentFunctions.returnStockToProducts(this.fulfillmentResult.products, this.fulfillmentResult.fulfilled.stock_transfers[id].products)
- this.$delete(this.fulfillmentResult.fulfilled.stock_transfers, id)
- }
- this.$emit('remove-sales-order', id)
- },
- // FULFILLMENT TITLE BAR
- closeFulfillment () {
- this.$emit('close-dialog')
- },
- submitFulfillment () {
- this.confirmDialog = true
- },
- confirmFulfillment () {
- // CHECK FOR ONE SUCCESS ORDERS
- if (_.isEmpty(this.successOrders)) {
- this.callSnackbar('AT LEAST 1 ORDER STATUS MUST BE SUCCESS')
- return
- }
- this.fullscreenLoading = true
- this.$store.dispatch('sales/fulfillment', {
- // TO DO: research about voucher implementation in fulfillment, ask SUDONO
- routing_type: this.routingMethod,
- orders: this.orders,
- orders_total_price: this.totalFulfilledPrice,
- orders_total_price_percent: this.totalFulfilledPricePercent,
- orders_total_qty_percent: this.totalFulfilledQtyPercent,
- fulfilled: this.fulfillmentResult.fulfilled,
- deliveryDate: this.deliveryDate.selected,
- order_type: (this.type === 'st') ? 'stock_transfers' : 'sales_orders',
- voucher_validity: Object.keys(this.orders)
- .filter(salesOrderId => this.orders[salesOrderId].voucher)
- .reduce((a, b) => {
- a[b] = this.isVoucherStatusValid(this.fulfillmentResult.fulfilled.sales_orders[b].products, b)
- return a
- }, {})
- })
- .then(result => {
- let param = { success: false }
- if (result.status === 201 || result.status === 200) {
- param.success = true
- if (this.routingMethod === 'manual') {
- param.delivery_order_id = result.data.delivery_order_id
- } else {
- param.invoices = result.data.sales_orders
- }
- } else {
- param.success = false
- param.error = 'Error Code ' + result.status + ': ' + result.message
- }
- setTimeout(() => {
- this.fullscreenLoading = false
- this.$emit('finish-fulfillment', param)
- }, 1500)
- })
- },
- addInvalidRuleText (salesOrderId, value) {
- const invalidReasons = this.invalidReasons[salesOrderId]
- if (!Array.isArray(invalidReasons)) {
- this.invalidReasons[salesOrderId] = []
- }
- const index = invalidReasons.indexOf(value)
- if (index === -1) {
- invalidReasons.push(value)
- }
- },
- removeInvalidRuleText (salesOrderId, value) {
- const invalidReasons = this.invalidReasons[salesOrderId]
- if (!Array.isArray(invalidReasons)) {
- this.invalidReasons[salesOrderId] = []
- }
- const index = invalidReasons.indexOf(value)
- if (index > -1) {
- invalidReasons.splice(index, 1)
- }
- },
- // checkProductGeneralRule (salesOrderId, productDetails, soProducts, invalidText) {
- // for (const productDetail of productDetails) {
- // const product = soProducts[productDetail.id]
- // if (!product) {
- // this.addInvalidRuleText(salesOrderId, invalidText)
- // continue
- // }
- // const productQty = Number.parseInt(product.qty)
- // if (productQty <= 0) {
- // this.addInvalidRuleText(salesOrderId, invalidText)
- // continue
- // } else {
- // this.removeInvalidRuleText(salesOrderId, invalidText)
- // continue
- // }
- // }
- // },
- // processInvalidSo (valueName, invalidText, salesOrderId, soProducts, productDetails) {
- // const invalidTextComplete = invalidText + valueName
- // this.addInvalidRuleText(salesOrderId, invalidTextComplete)
- // this.checkProductGeneralRule(salesOrderId, productDetails, soProducts, invalidTextComplete)
- // },
- isVoucherStatusValid (soProducts, salesOrderId) {
- if (this.type === 'st') {
- return true
- }
- this.invalidReasons[salesOrderId] = []
- const salesOrder = this.orders[salesOrderId]
- const voucherSO = salesOrder.voucher
- const soProductIds = Object.keys(soProducts)
- const soProductDetails = this.productDetails.filter(value => soProductIds.indexOf(value.id) > -1)
- soProductDetails.forEach(productDetail => { productDetail.combined_category_id = `${productDetail.department_id}${productDetail.category_id}` })
- if (
- !soProducts ||
- !voucherSO ||
- !voucherSO.rules ||
- !voucherSO.rules.order
- ) {
- return true
- }
- // Check benefit products
- const benefit = voucherSO.benefit || {}
- const discount = benefit.discount || {}
- if (discount.scope === 'product') {
- // Check if all products in scope are not fulfilled then the voucher is invalid
- const orderedDiscountProduct = discount.products.filter(product => soProducts[product.id])
- const isDiscountProductFulfilled = orderedDiscountProduct.reduce((isValid, discountProduct) => {
- const productId = discountProduct.id
- return isValid || soProducts[productId].qty > 0
- }, false)
- if (!isDiscountProductFulfilled) {
- const orderedDiscountProductText = `<ol>${orderedDiscountProduct.map(discountProduct => `<li>${discountProduct.name}</li>`).join('')}</ol>`
- const reason = `The discounted products not fulfilled: <br/>${orderedDiscountProductText}`
- this.addInvalidRuleText(salesOrderId, reason)
- }
- }
- // Check rules
- const rulesOrder = voucherSO.rules.order
- const scope = rulesOrder.scope
- const orderType = rulesOrder.type
- const ruleMinimumAmount = Number.parseFloat(rulesOrder.minimum_amount || 0)
- const minimumAmount = (80 / 100) * ruleMinimumAmount
- const productExclusions = scope === 'product' && orderType === 'all' ? (rulesOrder.metadata.product_exclusion || []) : []
- const excludedTotalPrice = productExclusions.reduce((sumExcluded, excludedProduct) => {
- const product = soProducts[excludedProduct.id]
- const totalProductPrice = product ? product.units.reduce((sum, unit) => {
- const fulfilledQuantity = unit.fulfilled_quantity
- const price = unit.price
- return (fulfilledQuantity * price) + sum
- }, 0) : 0
- return sumExcluded + totalProductPrice
- }, 0)
- const fulfilledTotalPrice = Number.parseFloat(this.totalFulfilledPrice[salesOrderId]) - Number.parseFloat(excludedTotalPrice)
- const isTotalAndMinimumValid = !Number.isNaN(minimumAmount) && !Number.isNaN(fulfilledTotalPrice)
- const isFulfilledTotalReach80Percent = (isTotalAndMinimumValid === true) ? (minimumAmount <= fulfilledTotalPrice) : true
- // Validate minimum total fulfilled amount of 80%
- if (!isFulfilledTotalReach80Percent) {
- const excludedProductsText = `<ol>${productExclusions.map(excludedProduct => `<li>${excludedProduct.name}</li>`).join('')}</ol>`
- const reason = `Total fulfilled < 80%${productExclusions.length > 0 ? `, excluded:<br/>${excludedProductsText}` : ''}`
- this.addInvalidRuleText(salesOrderId, reason)
- }
- // Validate there is at least 1 product fulfilled (except excluded products)
- if (fulfilledTotalPrice <= 0) {
- const reason = `There is no product fulfilled except the excluded ones`
- this.addInvalidRuleText(salesOrderId, reason)
- }
- // included product
- const productCondition = rulesOrder.metadata.logic
- if (scope === 'category' || scope === 'brand') {
- const scopeKey = (scope === 'category') ? 'combined_category_id' : 'brand_id'
- const metadataValues = voucherSO.rules.order.metadata.values.map(value => ({
- id: scope === 'category' ? `${value.department_id}${value.id}` : value.id,
- name: value.name
- }))
- const metadataValuesIds = metadataValues.map(value => value.id)
- const fulfilledProductDetails = soProductDetails.filter(productDetail => soProducts[productDetail.id].qty > 0)
- const soProductsValuesIds = fulfilledProductDetails.map(value => value[scopeKey])
- const metadataValuesNotExist = metadataValuesIds.filter(
- value => soProductsValuesIds.indexOf(value) === -1)
- const fulfilledScopedProductDetails = fulfilledProductDetails.filter(
- value => metadataValuesIds.indexOf(value[scopeKey]) > -1)
- const productDetailsValuesIds = fulfilledScopedProductDetails.map(value => value[scopeKey])
- const isAllValuesExist = (productDetailsValuesIds.length === metadataValuesIds.length)
- const isAllValuesNotExist = metadataValuesIds.length === metadataValuesNotExist.length
- if (productCondition === 'and' && isAllValuesExist === false) {
- const missingMetadataValues = metadataValues.filter(value => metadataValuesNotExist.includes(value.id))
- const reason = `These ${scope === 'category' ? 'categories' : 'brands'} not fulfilled:<br/><ol>${missingMetadataValues.map(value => `<li>${value.name}</li>`).join('')}</ol>`
- this.addInvalidRuleText(salesOrderId, reason)
- } else if (productCondition === 'or' && isAllValuesNotExist === true) {
- const reason = `Fulfilled at least one of these ${scope === 'category' ? 'categories' : 'brands'}:<br/><ol>${metadataValues.map(value => `<li>${value.name}</li>`).join('')}</ol>`
- this.addInvalidRuleText(salesOrderId, reason)
- }
- }
- // if (scope === 'product' && orderType === 'all') {
- // const productExclusion = rulesOrder.metadata.product_exclusion ? rulesOrder.metadata.product_exclusion : []
- // const productIdsNotFound = productExclusion.filter(value => soProductIds.indexOf(value.id) === -1)
- // if (productIdsNotFound.length > 0) {
- // this.invalidReasons[salesOrderId].push(...productIdsNotFound.map(value => value.name))
- // }
- // }
- if (scope === 'product' && orderType === 'include') {
- const minimumSelection = rulesOrder.metadata.minimum_selection
- if (minimumSelection === 'total') {
- const minimumTotalValue = Number.parseInt(rulesOrder.metadata.minimum_total_value)
- const metadataValues = voucherSO.rules.order.metadata.values
- const soProductsValue = Object.values(soProducts).filter(product => metadataValues.find(value => product.product_id === value.id))
- const totalValue = soProductsValue.reduce(
- (total, value) => value.units.reduce(
- (uTotal, uValue) => (Number.parseInt(uValue.fulfilled_quantity) * Number.parseInt(uValue.price)) + uTotal
- , total)
- , 0)
- if (totalValue < (0.8 * minimumTotalValue)) {
- // Voucher invalid in this state
- const reason = `Total fulfilled of these products is not enough (< 80% of minimum voucher condition): <br/><ol>${soProductsValue.map(value => `<li>${value.name}</li>`).join('')}</ol>`
- this.invalidReasons[salesOrderId].push(reason)
- }
- const notFulfilledProducts = soProductsValue.filter(product => product.qty <= 0)
- if (notFulfilledProducts.length > 0) {
- const reason = `These products is not fulfilled at least 1: <br/><ol>${notFulfilledProducts.map(value => `<li>${value.name}</li>`).join('')}</ol>`
- this.invalidReasons[salesOrderId].push(reason)
- }
- }
- if (minimumSelection === 'individual') {
- const metadataValues = voucherSO.rules.order.metadata.values
- const validProducts = []
- const invalidProducts = []
- for (const value of metadataValues) {
- const fulfilled = soProducts[value.id]
- // This is for the "or" type where the product can be not ordered
- if (!fulfilled) {
- // If it is not ordered, skip this
- // invalidProducts.push(value)
- continue
- }
- const totalUnitPrice = fulfilled.units.reduce(
- (uTotal, uValue) => (Number.parseInt(uValue.fulfilled_quantity) * Number.parseInt(uValue.price)) + uTotal
- , 0)
- const isTotalPriceReachMinimum = totalUnitPrice >= (0.8 * Number.parseInt(value.minimum_value))
- if (isTotalPriceReachMinimum) {
- validProducts.push(value)
- } else {
- invalidProducts.push(value)
- }
- }
- // "OR" is invalid when there is no valid products
- if (productCondition === 'or' && validProducts.length <= 0) {
- const thousandSeparator = this.$options.filters.thousandSeparator
- const reason = `Any of these conditions is not fulfilled: <br/><ol>${invalidProducts.map(value => `<li>${value.name} - minimum Rp ${thousandSeparator(value.minimum_value)}</li>`).join('')}</ol>`
- this.addInvalidRuleText(salesOrderId, reason)
- } else if (productCondition === 'and' && invalidProducts.length > 0) {
- const thousandSeparator = this.$options.filters.thousandSeparator
- const reason = `All of these conditions are not fulfilled: <br/><ol>${invalidProducts.map(value => `<li>${value.name} - minimum Rp ${thousandSeparator(value.minimum_value)}</li>`).join('')}</ol>`
- this.addInvalidRuleText(salesOrderId, reason)
- }
- }
- if (minimumSelection === 'none') {
- const metadataValues = voucherSO.rules.order.metadata.values
- const validProducts = []
- const invalidProducts = []
- for (const value of metadataValues) {
- const fulfilled = soProducts[value.id]
- // This is for the "or" type where the product can be not ordered
- if (!fulfilled) {
- // If it is not ordered, skip this
- // invalidProducts.push(value)
- continue
- }
- const isFulfilled = fulfilled.qty > 0
- if (isFulfilled) {
- validProducts.push(value)
- } else {
- invalidProducts.push(value)
- }
- }
- if (productCondition === 'or' && validProducts.length <= 0) {
- const reason = `Any of these products is not fulfilled: <br/><ol>${invalidProducts.map(value => `<li>${value.name}</li>`).join('')}</ol>`
- this.addInvalidRuleText(salesOrderId, reason)
- } else if (productCondition === 'and' && invalidProducts.length > 0) {
- const reason = `All of these products are not fulfilled: <br/><ol>${invalidProducts.map(value => `<li>${value.name}</li>`).join('')}</ol>`
- this.addInvalidRuleText(salesOrderId, reason)
- }
- }
- }
- // Voucher Product Condition: "Doesn't Contains Products"
- if (scope === 'product' && orderType === 'exclude') {
- // Do nothing.
- // Should be already handled in create order
- }
- // let isIncludedProductsExist = true
- // const isIncludedProductRuleExist = voucherSO.rules.order.products && Array.isArray(voucherSO.rules.order.products.included)
- // if (isIncludedProductRuleExist) {
- // const ruleIncludedProducts = voucherSO.rules.order.products.included
- // for (const ruleIncludedProduct of ruleIncludedProducts) {
- // const productId = ruleIncludedProduct.id
- // const product = soProducts[productId]
- // if (!product) {
- // this.invalidReasons[salesOrderId].push(ruleIncludedProduct.name)
- // isIncludedProductsExist = false
- // continue
- // }
- // const productQty = Number.parseInt(product.qty)
- // if (productQty <= 0) {
- // this.invalidReasons[salesOrderId].push(ruleIncludedProduct.name)
- // isIncludedProductsExist = false
- // continue
- // }
- // }
- // }
- if (this.invalidReasons[salesOrderId].length !== 0) {
- return false
- }
- return true
- },
- isFulfilledQtyValid (fulfilled, request, soId, productId) {
- const fulfilledValue = parseInt(fulfilled)
- const requestValue = parseInt(request)
- const fulfilledPercentage = (fulfilledValue / requestValue) * 100
- if (!this.totalFulfilledQtyPercent[soId]) {
- this.totalFulfilledQtyPercent[soId] = {}
- }
- this.totalFulfilledQtyPercent[soId][productId] = fulfilledPercentage
- return fulfilledPercentage >= 80
- },
- isFulfilledPriceValid (fulfilled, request, soId, productId) {
- const fulfilledValue = parseInt(fulfilled)
- const requestValue = parseInt(request)
- let itemPrice = 0
- if (this.orders[soId].products_requested && this.orders[soId].products_requested[productId]) {
- itemPrice = parseInt(this.orders[soId].products_requested[productId].selling_price) | 0
- }
- const fulfilledPrice = itemPrice * fulfilledValue
- const requestPrice = itemPrice * requestValue
- const pricePercentage = (fulfilledPrice / requestPrice) * 100
- if (pricePercentage < 80) {
- return false
- }
- return true
- },
- isFulfilledStatusValid (fulfilled, request, soId, productId) {
- if (!this.totalFulfilledPrice[soId]) {
- this.recalculateTotalPrice()
- }
- return request === fulfilled
- },
- recalculateTotalPrice () {
- const salesOrders = this.fulfillmentResult.fulfilled.sales_orders
- for (const salesOrderKey in salesOrders) {
- this.totalFulfilledPrice[salesOrderKey] = 0
- const products = salesOrders[salesOrderKey].products
- for (const productKey in products) {
- const totalProductPrice = products[productKey].units.reduce((sum, unit) => {
- const fulfilledQuantity = unit.fulfilled_quantity
- const price = unit.price
- return (fulfilledQuantity * price) + sum
- }, 0)
- this.totalFulfilledPrice[salesOrderKey] += totalProductPrice
- }
- }
- },
- closeVoucherErrorMessageDialog () {
- this.voucherErrorDialogShow = false
- this.dialogFulfill = true
- }
- }
- }
- </script>
- <style scoped>
- table {
- min-width: 100%;
- }
- table.products-table,
- table.summary-products-table {
- border-collapse: collapse;
- }
- table.products-table th:first-child,
- table.products-table td:first-child {
- padding-left: 0px;
- min-width: 170px;
- }
- table th {
- text-align: left;
- text-transform: uppercase;
- border-bottom: 1px solid;
- padding: 5px;
- border-color: #eaeaea;
- }
- table.summary-products-table th:first-child,
- table.summary-products-table td:first-child {
- padding: 4px 24px 4px 0;
- }
- table.summary-products-table th:last-child,
- table.summary-products-table td:last-child {
- padding-left: 0px;
- padding-right: 0px;
- }
- table.summary-products-table td,
- table.summary-products-table th {
- height: 36px;
- }
- table.summary-products-table td {
- min-width: 90px;
- }
- table.summary-products-table tr {
- border-bottom: none !important;
- }
- >>>.solo-outline .v-text-field__slot {
- padding: 4px;
- }
- /* table td {
- padding: 5px;
- } */
- </style>
Editor
You can edit this paste and save as new: