Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 115 additions & 1 deletion posawesome/fixtures/custom_field.json
Original file line number Diff line number Diff line change
Expand Up @@ -6183,7 +6183,121 @@
"translatable": 0,
"unique": 0,
"width": null
}
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "Sales Invoice",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "custom_location",
"fieldtype": "Data",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "po_no",
"is_system_generated": 0,
"is_virtual": 0,
"label": "Location",
"length": 0,
"link_filters": null,
"mandatory_depends_on": null,
"modified": "2025-07-11 12:05:43.030076",
"module": "POSAwesome",
"name": "Sales Invoice-custom_location",
"no_copy": 0,
"non_negative": 0,
"options": null,
"permlevel": 0,
"placeholder": null,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"show_dashboard": 0,
"sort_options": 0,
"translatable": 1,
"unique": 0,
"width": null
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"doctype": "Custom Field",
"dt": "POS Profile",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "custom_require_customer_po_details",
"fieldtype": "Check",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "custom_alternative_item_bundle",
"is_system_generated": 0,
"is_virtual": 0,
"label": "Require Customer Po Details",
"length": 0,
"link_filters": null,
"mandatory_depends_on": null,
"modified": "2025-07-11 12:05:12.254810",
"module": "POSAwesome",
"name": "POS Profile-custom_require_customer_po_details",
"no_copy": 0,
"non_negative": 0,
"options": null,
"permlevel": 0,
"placeholder": null,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
}


]
2 changes: 2 additions & 0 deletions posawesome/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@
"POS Profile-custom_show_last_incoming_rate",
"POS Profile-custom_show_logical_rack",
"POS Profile-custom_show_last_custom_rate",
"POS Profile-custom_require_customer_po_details",
"Sales Invoice-custom_location",
),
]
],
Expand Down
136 changes: 135 additions & 1 deletion posawesome/posawesome/api/posapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,23 @@ def submit_invoice(invoice, data):
else:
invoice_doc = frappe.get_doc("Sales Invoice", invoice_name)
invoice_doc.update(invoice)

if invoice.get("po_no"):
invoice_doc.po_no = invoice.get("po_no")

if invoice.get("custom_location"):
invoice_doc.custom_location = invoice.get("custom_location")

# Update reference details
if invoice.get("custom_reference_no"):
invoice_doc.custom_reference_no = invoice.get("custom_reference_no")

if invoice.get("custom_reference_name"):
invoice_doc.custom_reference_name = invoice.get("custom_reference_name")

if invoice.get("posa_delivery_date"):
invoice_doc.update_stock = 0

if invoice.get("posa_delivery_date"):
invoice_doc.update_stock = 0
mop_cash_list = [
Expand All @@ -932,6 +949,13 @@ def submit_invoice(invoice, data):
if item.item_name and item.rate and item.qty:
total = item.rate * item.qty
items.append(f"{item.item_name} - Rate: {item.rate}, Qty: {item.qty}, Amount: {total}")

# Add customer PO details to remarks if available
if invoice_doc.po_no:
items.append(f"Customer PO: {invoice_doc.po_no}")

if invoice_doc.custom_location:
items.append(f"Location: {invoice_doc.custom_location}")

# Add the grand total at the end of remarks
grand_total = f"\nGrand Total: {invoice_doc.grand_total}"
Expand Down Expand Up @@ -2804,4 +2828,114 @@ def get_last_customer_rate_value(customer, item_code):

except Exception as e:
frappe.log_error(f"Error getting last customer rate for {customer}, {item_code}: {str(e)}")
return {"last_customer_rate": 0}
return {"last_customer_rate": 0}

@frappe.whitelist()
def update_invoice_with_customer_po(data):
"""Update invoice with customer PO details and reference details"""
data = json.loads(data)

if data.get("name"):
invoice_doc = frappe.get_doc("Sales Invoice", data.get("name"))
invoice_doc.update(data)
else:
invoice_doc = frappe.get_doc(data)

# Handle customer PO details
if data.get("po_no"):
invoice_doc.po_no = data.get("po_no")

if data.get("custom_location"):
invoice_doc.custom_location = data.get("custom_location")

# Handle reference details
if data.get("custom_reference_no"):
invoice_doc.custom_reference_no = data.get("custom_reference_no")

if data.get("custom_reference_name"):
invoice_doc.custom_reference_name = data.get("custom_reference_name")

# Validate return items if this is a return invoice
if (data.get("is_return") or invoice_doc.is_return) and invoice_doc.get("return_against"):
validation = validate_return_items(invoice_doc.return_against, [d.as_dict() for d in invoice_doc.items])
if not validation.get("valid"):
frappe.throw(validation.get("message"))

selected_currency = data.get("currency")

# Set missing values first
invoice_doc.set_missing_values()

# Ensure selected currency is preserved after set_missing_values
if selected_currency:
invoice_doc.currency = selected_currency
# Get default conversion rate from ERPNext if currency is different from company currency
if invoice_doc.currency != frappe.get_cached_value("Company", invoice_doc.company, "default_currency"):
company_currency = frappe.get_cached_value("Company", invoice_doc.company, "default_currency")

# Determine price list currency
price_list_currency = data.get("price_list_currency")
if not price_list_currency and invoice_doc.get("selling_price_list"):
price_list_currency = frappe.db.get_value(
"Price List", invoice_doc.selling_price_list, "currency"
)
if not price_list_currency:
price_list_currency = company_currency

conversion_rate = 1
if invoice_doc.currency != company_currency:
conversion_rate = get_exchange_rate(
invoice_doc.currency,
company_currency,
invoice_doc.posting_date,
)

plc_conversion_rate = 1
if price_list_currency != invoice_doc.currency:
plc_conversion_rate = get_exchange_rate(
price_list_currency,
invoice_doc.currency,
invoice_doc.posting_date,
)

invoice_doc.conversion_rate = conversion_rate
invoice_doc.plc_conversion_rate = plc_conversion_rate
invoice_doc.price_list_currency = price_list_currency

# Update rates and amounts for all items using division
for item in invoice_doc.items:
if item.price_list_rate:
item.base_price_list_rate = flt(
item.price_list_rate * (conversion_rate / plc_conversion_rate),
item.precision("base_price_list_rate"),
)
if item.rate:
item.base_rate = flt(item.rate * conversion_rate, item.precision("base_rate"))
if item.amount:
item.base_amount = flt(item.amount * conversion_rate, item.precision("base_amount"))

# Update payment amounts
for payment in invoice_doc.payments:
payment.base_amount = flt(payment.amount * conversion_rate, payment.precision("base_amount"))

# Update invoice level amounts
invoice_doc.base_total = flt(invoice_doc.total * conversion_rate, invoice_doc.precision("base_total"))
invoice_doc.base_net_total = flt(invoice_doc.net_total * conversion_rate, invoice_doc.precision("base_net_total"))
invoice_doc.base_grand_total = flt(invoice_doc.grand_total * conversion_rate, invoice_doc.precision("base_grand_total"))
invoice_doc.base_rounded_total = flt(invoice_doc.rounded_total * conversion_rate, invoice_doc.precision("base_rounded_total"))
invoice_doc.base_in_words = money_in_words(invoice_doc.base_rounded_total, invoice_doc.company_currency)

# Update data to be sent back to frontend
data["conversion_rate"] = conversion_rate
data["plc_conversion_rate"] = plc_conversion_rate

invoice_doc.flags.ignore_permissions = True
frappe.flags.ignore_account_permission = True
invoice_doc.docstatus = 0
invoice_doc.save()

# Return both the invoice doc and the updated data
response = invoice_doc.as_dict()
response["conversion_rate"] = invoice_doc.conversion_rate
response["plc_conversion_rate"] = invoice_doc.plc_conversion_rate
return response
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!-- CustomerValidationModal.vue -->
<template>
<v-dialog v-model="show" max-width="400" persistent>
<v-card>
<v-card-title class="text-h6 pa-4 d-flex align-center bg-warning">
<v-icon left color="white">mdi-alert</v-icon>
<span class="white--text">{{ __('Customer Required') }}</span>
<v-spacer></v-spacer>
</v-card-title>

<v-card-text class="pa-6 text-center">
<v-icon size="64" color="warning" class="mb-4">mdi-account-alert</v-icon>
<h3 class="mb-3">{{ __('Customer First Select') }}</h3>
<p class="text-body-1 mb-0">
{{ __('Please select a customer before adding items to the invoice.') }}
</p>
</v-card-text>

<v-card-actions class="pa-4 pt-0">
<v-spacer></v-spacer>
<v-btn
color="primary"
variant="elevated"
@click="closeModal"
prepend-icon="mdi-check"
>
{{ __('OK') }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>

<script>
export default {
name: 'CustomerValidationModal',
props: {
modelValue: {
type: Boolean,
default: false
}
},
computed: {
show: {
get() {
return this.modelValue;
},
set(value) {
this.$emit('update:modelValue', value);
}
}
},
methods: {
closeModal() {
this.show = false;
}
}
};
</script>

<style scoped>
.bg-warning {
background-color: rgb(var(--v-theme-warning)) !important;
}

.white--text {
color: white !important;
}
</style>
Loading
Loading