Added functionality to lost Purchase Order Items

This commit is contained in:
2024-09-27 10:22:14 +00:00
parent cd98dffbab
commit b1935a3520
3 changed files with 256 additions and 108 deletions

View File

@@ -2,11 +2,8 @@ import frappe
from functools import reduce from functools import reduce
from datetime import date from datetime import date
from erpnext.manufacturing.doctype.bom.bom import get_bom_items, BOM, get_bom_items_as_dict # frappe.utils.logger.set_log_level("DEBUG")
from erpnext.stock.get_item_details import get_default_bom # logger = frappe.logger("manufacturing_overview", allow_site=True)
frappe.utils.logger.set_log_level("DEBUG")
logger = frappe.logger("manufacturing_overview", allow_site=True)
#logger.debug(f"String") #logger.debug(f"String")
@@ -50,7 +47,7 @@ def generateProductionOverviewCacheData():
`tabSales Order Item`.qty, `tabSales Order Item`.qty,
`tabSales Order Item`.actual_qty, `tabSales Order Item`.actual_qty,
`tabSales Order Item`.delivered_qty, `tabSales Order Item`.delivered_qty,
`tabSales Order Item`.delivery_date, `tabSales Order Item`.delivery_date AS date,
`tabSales Order Item`.item_code, `tabSales Order Item`.item_code,
`tabSales Order Item`.work_order_qty, `tabSales Order Item`.work_order_qty,
`tabSales Order Item`.parent, `tabSales Order Item`.parent,
@@ -66,7 +63,7 @@ def generateProductionOverviewCacheData():
status = 'To Deliver and Bill' and status = 'To Deliver and Bill' and
item_group = "Produkte" item_group = "Produkte"
ORDER BY ORDER BY
delivery_date, item_code, name date, item_code, name
""", as_dict=1) """, as_dict=1)
@@ -78,7 +75,7 @@ def generateProductionOverviewCacheData():
soItem.totalWarehouseQty = getAmountInWarehouses(soItem.item_code) soItem.totalWarehouseQty = getAmountInWarehouses(soItem.item_code)
soItem.due_in = getDueInDays(soItem.delivery_date) soItem.due_in = getDueInDays(soItem.date)
soItem.customer = shortenCustomerName(frappe.get_value( soItem.customer = shortenCustomerName(frappe.get_value(
'Sales Order', soItem.parent, 'customer')) 'Sales Order', soItem.parent, 'customer'))
@@ -104,6 +101,44 @@ def generateProductionOverviewCacheData():
return salesOrderItems return salesOrderItems
def generatePurchaseOrderOverviewCacheData():
purchaseOrderItems = frappe.db.sql(
"""
SELECT
`tabPurchase Order Item`.name,
`tabPurchase Order Item`.item_name,
`tabPurchase Order Item`.item_group,
`tabPurchase Order Item`.qty,
`tabPurchase Order Item`.schedule_date AS date,
`tabPurchase Order Item`.item_code,
`tabPurchase Order Item`.parent,
`tabPurchase Order Item`.fg_item,
`tabPurchase Order`.status as parentStatus
FROM
`tabPurchase Order Item`
INNER JOIN `tabPurchase Order`
ON
`tabPurchase Order Item`.parent = `tabPurchase Order`.name
WHERE
status = 'To Receive and Bill'
ORDER BY
date, item_code, name
""", as_dict=1)
for poItem in purchaseOrderItems:
poItem = formatDate(poItem)
poItem.customer = frappe.get_value("Purchase Order", poItem.parent, 'supplier_name')
poItem.fg_item_name = frappe.get_value("Item", poItem.fg_item, 'item_name')
poItem.direct_po = poItem.parent
poItem.link = '/app/purchase-order/' + poItem.parent
poItem.status = 'Unknown'
return purchaseOrderItems
def calculateCurrentWarehouseQty(item_code, qty, currentWarehouseQtyList): def calculateCurrentWarehouseQty(item_code, qty, currentWarehouseQtyList):
for warehouseQty in currentWarehouseQtyList: for warehouseQty in currentWarehouseQtyList:
@@ -119,8 +154,8 @@ def calculateCurrentWarehouseQty(item_code, qty, currentWarehouseQtyList):
def formatDate(item): def formatDate(item):
item.delivery_date = frappe.utils.formatdate( item.date = frappe.utils.formatdate(
item.delivery_date, 'dd.MM.yyyy') item.date, 'dd.MM.yyyy')
return item return item
@@ -144,36 +179,16 @@ def getSalesorderOverviewList():
if salesOrderItems is None: if salesOrderItems is None:
salesOrderItems = generateProductionOverviewCacheData() salesOrderItems = generateProductionOverviewCacheData()
frappe.cache().set_value('production_overview', salesOrderItems, expires_in_sec=1) frappe.cache().set_value('production_overview', salesOrderItems,expires_in_sec=600)
return salesOrderItems return salesOrderItems
@frappe.whitelist() @frappe.whitelist()
def get_bom_tree(item_code): def getPurchaseOrderOverviewList():
bom_name = get_default_bom(item_code) purchaseOrderItems = frappe.cache().get_value('purchaseOrder_items', expires=True)
root_bom = frappe.get_doc("BOM", bom_name)
if root_bom != None:
bom_tree = BOM.traverse_tree(root_bom) # Gets all BOM in correct order
bomlist = [] if purchaseOrderItems is None:
purchaseOrderItems = generatePurchaseOrderOverviewCacheData()
frappe.cache().set_value('purchaseOrder_items', purchaseOrderItems, expires_in_sec=600)
for bom_item in bom_tree: return purchaseOrderItems
bom = frappe.get_doc("BOM", bom_item)
bomlist.append(bom)
# item = frappe.get_doc("Item", bom)
# current_bom_item = frappe.get_doc("BOM", 'BOM-0815-3-001').get_tree_representation()
# bom_items = get_bom_items("BOM-0815-3-001", "Hans Prothmann GmbH") #Fetches all raw materials
# routing = BOM.get_routing(frm) #Gets alls operations
# test = get_bom_items_as_dict(bom_name, "Hans Prothmann GmbH") # Gets all rm items as a dict
# bomtreeitem = BOM.get_tree_representation(bom_name)
logger.debug(bomlist)
# logger.debug(bomtree)
return bomlist

View File

@@ -1,23 +1,96 @@
<template> <template>
<div class="layout-main-section"> <div class="form-tabs-list">
<div class="widget quick-list-widget-box" style="height: auto"> <ul class="nav form-tabs" id="form-tabs" role="tablist">
<div class="widget-head"> <li class="nav-item show">
<div class="widget-label"> <a
<div class="widget-title"> class="nav-link"
<div class="widget-title"><span class="ellipsis" title="Sales Order">Delivery Overview</span></div> :class="{ active: currentTab === 0 }"
<div class="widget-subtitle"></div> role="tab"
@click="changeTab(0)"
>
Production Overview
</a>
</li>
<li class="nav-item show">
<a
class="nav-link"
:class="{ active: currentTab === 1 }"
role="tab"
@click="changeTab(1)"
>
Purchase Overview
</a>
</li>
</ul>
<div class="layout-main-section">
<div
class="widget quick-list-widget-box"
style="height: auto"
v-if="currentTab === 0"
>
<div class="widget-head">
<div class="widget-label">
<div class="widget-title">
<span class="ellipsis" title="Sales Order"
>Delivery Overview</span
>
</div>
</div> </div>
<div class="widget-subtitle"></div> <div class="widget-control"></div>
</div>
<div class="widget-body">
<manufacturing-overview-row
v-for="so in salesorderData"
:key="so.name"
v-bind:qty="so.qty"
v-bind:item_name="so.item_name"
v-bind:item_code="so.item_code"
v-bind:customer="so.customer"
v-bind:date="so.date"
v-bind:status="so.status"
v-bind:link="so.link"
v-bind:reference="so.parent"
v-bind:due_in="so.due_in"
v-bind:direct_wo="so.direct_wo"
v-bind:direct_po="so.direct_po"
v-bind:rowtype="so"
>
</manufacturing-overview-row>
</div> </div>
<div class="widget-control"></div>
</div> </div>
<div class="widget-body">
<manufacturing-overview-row v-for="so in salesorderData" :key="so.name" v-bind:qty="so.qty" <div
v-bind:item_name="so.item_name" v-bind:item_code="so.item_code" v-bind:customer="so.customer" class="widget quick-list-widget-box"
v-bind:delivery_date="so.delivery_date" v-bind:status="so.status" v-bind:link="so.link" style="height: auto"
v-bind:reference="so.parent" v-bind:due_in="so.due_in" v-bind:direct_wo="so.direct_wo" v-if="currentTab === 1"
v-bind:direct_po="so.direct_po"> >
</manufacturing-overview-row> <div class="widget-head">
<div class="widget-label">
<div class="widget-title">
<span class="ellipsis" title="Sales Order"
>Purchase Overview</span
>
</div>
</div>
<div class="widget-control"></div>
</div>
<div class="widget-body">
<manufacturing-overview-row
v-for="sc in purchaseOrderData"
:key="sc.name"
v-bind:qty="sc.qty"
v-bind:item_name="sc.fg_item_name"
v-bind:item_code="sc.fg_item"
v-bind:fg_item="sc.fg_item"
v-bind:customer="sc.customer"
v-bind:date="sc.date"
v-bind:status="sc.status"
v-bind:direct_po="sc.direct_po"
v-bind:link="sc.link"
v-bind:rowtype="po"
>
</manufacturing-overview-row>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -32,10 +105,11 @@ export default {
}, },
data() { data() {
return { return {
currentTab: 0,
salesorderData: [ salesorderData: [
{ {
customer: "", customer: "",
delivery_date: "", date: "",
link: "", link: "",
name: "", name: "",
item_name: "Loading...", item_name: "Loading...",
@@ -48,6 +122,20 @@ export default {
direct_po: "", direct_po: "",
}, },
], ],
purchaseOrderData: [
{
name: "",
customer: "",
link: "",
item_name: "Loading...",
item_code: "",
qty: "",
date: "",
fg_item: "",
status: "Unknown",
direct_po: "",
},
],
timer: "", timer: "",
origin: window.location.origin, origin: window.location.origin,
userPermissions: {}, userPermissions: {},
@@ -55,9 +143,13 @@ export default {
}, },
created() { created() {
this.fetchEventsList(); this.fetchEventsList();
this.timer = setInterval(this.fetchEventsList, 10000); this.timer = setInterval(this.fetchEventsList, 30000);
}, },
methods: { methods: {
changeTab(id) {
let self = this;
self.currentTab = id;
},
fetchEventsList() { fetchEventsList() {
let self = this; let self = this;
frappe.call({ frappe.call({
@@ -71,6 +163,19 @@ export default {
} }
}, },
}); });
frappe.call({
method:
"manufacturing_overview.manufacturing_overview.api.getPurchaseOrderOverviewList",
async: true,
args: {},
callback: function (r) {
if (r.message) {
self.purchaseOrderData = r.message;
console.log(r.message);
}
},
});
}, },
cancelAutoUpdate() { cancelAutoUpdate() {
clearInterval(this.timer); clearInterval(this.timer);

View File

@@ -1,24 +1,44 @@
<template> <template>
<div @click="pushRoute(link)" @mouseenter="hover = true" @mouseleave="hover = false, detailsOpen = false" <div
class="quick-list-item align-items-start"> @click="pushRoute(link)"
@mouseenter="hover = true"
@mouseleave="(hover = false), (detailsOpen = false)"
class="quick-list-item align-items-start"
>
<div class="col-1"> <div class="col-1">
<span class="indicator-pill no-margin" v-bind:class="{ <span
red: status === 'No Work Order', class="indicator-pill no-margin"
blue: status === 'Partially Delivered', v-bind:class="{
gray: status === 'Fully Delivered', red: status === 'No Work Order',
green: status === 'In Warehouse', blue: status === 'Partially Delivered',
yellow: status === 'To Produce', gray: status === 'Fully Delivered',
grey: status === 'Unknown', green: status === 'In Warehouse',
}"></span> yellow: status === 'To Produce',
grey: status === 'Unknown',
}"
></span>
</div> </div>
<div class="col col-xs-8"> <div class="col col-xs-6 col-lg-8 overflow-hidden">
<span class="ellipsis title"
<span class="ellipsis title"><b>{{ qty }}</b> - {{ item_name }}</span> ><b>{{ qty }}</b> - {{ item_name }}</span
>
<div> <div>
<small v-if="customer && item_code" class="color-secondary">{{ customer }} - <small v-if="fg_item" class="color-secondary">
<a class="underline-hover" @click="pushRoute('/app/item/' + item_code)" v-on:click.stop>{{ <a
item_code class="underline-hover"
}}</a> @click="pushRoute('/app/item/' + fg_item)"
v-on:click.stop
>{{ fg_item }}</a
>
</small>
<small v-else-if="customer && item_code" class="color-secondary"
>{{ customer }} -
<a
class="underline-hover"
@click="pushRoute('/app/item/' + item_code)"
v-on:click.stop
>{{ item_code }}</a
>
</small> </small>
<small v-else-if="customer" class="color-secondary">{{ <small v-else-if="customer" class="color-secondary">{{
customer customer
@@ -30,21 +50,30 @@
<div> <div>
<small> <small>
<span> <span>
<a class="underline-hover" @click="pushRoute('/app/sales-order/' + reference)" v-on:click.stop>{{ <a
reference class="underline-hover"
}}</a> @click="pushRoute('/app/sales-order/' + reference)"
v-on:click.stop
>{{ reference }}</a
>
</span> </span>
<span v-if="direct_wo"> <span v-if="direct_wo">
- -
<a class="underline-hover" @click="pushRoute('/app/work-order/' + direct_wo)" v-on:click.stop>{{ <a
direct_wo class="underline-hover"
}}</a> @click="pushRoute('/app/work-order/' + direct_wo)"
v-on:click.stop
>{{ direct_wo }}</a
>
</span> </span>
<span v-if="direct_po && direct_wo">-</span>
<span v-if="direct_po"> <span v-if="direct_po">
- <a
<a class="underline-hover" @click="pushRoute('/app/purchase-order/' + direct_po)" v-on:click.stop>{{ class="underline-hover"
direct_po @click="pushRoute('/app/purchase-order/' + direct_po)"
}}</a> v-on:click.stop
>{{ direct_po }}</a
>
</span> </span>
</small> </small>
</div> </div>
@@ -52,22 +81,22 @@
<!-- v-if="hover" --> <!-- v-if="hover" -->
<div class="text-muted ellipsis color-secondary col col-xs-3 text-right"> <div
class="text-muted ellipsis color-secondary col col-xs-5 col-lg-3 text-right"
>
<div class="d-flex justify-content-end flex-column align-items-end"> <div class="d-flex justify-content-end flex-column align-items-end">
<div class="mb-auto"> <div class="mb-auto">
<b v-if="due_in < 0" style="color: red">{{ delivery_date }}</b> <b v-if="due_in < 0" style="color: red">{{ date }}</b>
<b v-else-if="due_in === 0" style="color: black">{{ delivery_date }}</b> <b v-else-if="due_in === 0" style="color: black">{{ date }}</b>
<span v-else>{{ delivery_date }}</span> <span v-else>{{ date }}</span>
</div> </div>
<!-- <div @click="getItemDetails(item_code)" v-if="!detailsOpen" v-on:click.stop> <!-- <div @click="getItemDetails(item_code)" v-if="!detailsOpen" v-on:click.stop>
Details Details
</div> --> </div> -->
</div> </div>
</div> </div>
<div v-if="detailsOpen" :style="{ height: '500px' }"></div> <!-- <div v-if="detailsOpen" :style="{ height: '500px' }"></div> -->
</div> </div>
</template> </template>
@@ -78,7 +107,7 @@ export default {
return { return {
hover: false, hover: false,
detailsOpen: false, detailsOpen: false,
currentDetails: ['1', '2'] currentDetails: ["1", "2"],
}; };
}, },
props: [ props: [
@@ -87,34 +116,33 @@ export default {
"item_code", "item_code",
"customer", "customer",
"customershort", "customershort",
"delivery_date", "date",
"status", "status",
"link", "link",
"reference", "reference",
"due_in", "due_in",
"direct_wo", "direct_wo",
"direct_po", "direct_po",
"fg_item",
], ],
methods: { methods: {
pushRoute(link) { pushRoute(link) {
frappe.router.push_state(link); frappe.router.push_state(link);
}, },
getItemDetails(item) { // getItemDetails(item) {
this.detailsOpen = !this.detailsOpen // this.detailsOpen = !this.detailsOpen;
frappe.call({ // frappe.call({
method: // method:
"manufacturing_overview.manufacturing_overview.api.get_bom_tree", // "manufacturing_overview.manufacturing_overview.api.get_bom_tree",
async: true, // async: true,
args: { item_code: item }, // args: { item_code: item },
callback: function (r) { // callback: function (r) {
console.log(r.message) // console.log(r.message);
}, // },
}); // });
} // },
}, },
}; };
</script> </script>
<style> <style>