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
40 changes: 40 additions & 0 deletions rmax_custom/api/purchase_invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import frappe

@frappe.whitelist()
def create_single_purchase_invoice(receipt_names):
import json
if isinstance(receipt_names, str):
receipt_names = json.loads(receipt_names)

# Get first receipt for header info
pr = frappe.get_doc("Purchase Receipt", receipt_names[0])

# Create new Purchase Invoice
pi = frappe.new_doc("Purchase Invoice")
pi.supplier = pr.supplier
pi.company = pr.company
pi.currency = pr.currency
pi.buying_price_list = pr.buying_price_list
pi.remarks = "Created from: " + ", ".join(receipt_names)

# Get all items from all receipts
for receipt_name in receipt_names:
receipt = frappe.get_doc("Purchase Receipt", receipt_name)
for item in receipt.items:
pi.append("items", {
"item_code": item.item_code,
"item_name": item.item_name,
"description": item.description,
"qty": item.qty,
"rate": item.rate,
"uom": item.uom,
"warehouse": item.warehouse,
"purchase_receipt": receipt_name,
"purchase_receipt_item": item.name,
"expense_account": item.expense_account,
"cost_center": item.cost_center
})

pi.insert(ignore_permissions=True)
frappe.db.commit()
return pi.name
5 changes: 4 additions & 1 deletion rmax_custom/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
"Purchase Receipt": "public/js/purchase receipt.js",
"Landed Cost Voucher": "public/js/landed_cost_voucher.js"
}
doctype_list_js = {
"Purchase Receipt": "public/js/purchase_receipt_list.js"
}
# doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
# doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"}
Expand Down Expand Up @@ -295,4 +298,4 @@
]
]
}
]
]
136 changes: 79 additions & 57 deletions rmax_custom/public/js/purchase receipt.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,74 @@

//Purchase Receipt - Final GRN Button + Auto-cancel original on submit of new receipt
frappe.ui.form.on('Purchase Receipt', {
refresh: function(frm) {
if (frm.doc.docstatus === 1 || frm.doc.docstatus === 0) {
frm.add_custom_button(__('Final GRN'), function() {
new_purchase_receipt_with_data(frm);
}, __('Create'));
}
},

// Auto-cancel original when new receipt is SUBMITTED
on_submit: function(frm) {
let source_name = frm._source_name;
let source_docstatus = frm._source_docstatus;

if (!source_name) return;

if (source_docstatus === 1) {
frappe.call({
method: 'frappe.client.cancel',
args: { doctype: 'Purchase Receipt', name: source_name },
callback: function(r) {
if (!r.exc) {
frappe.show_alert({
message: __(source_name + ' automatically cancelled.'),
indicator: 'green'
}, 6);
frm._source_name = null;
} else {
frappe.msgprint(__('Could not auto-cancel ' + source_name + '. Please cancel manually.'));
}
}
});

} else if (source_docstatus === 0) {
frappe.call({
method: 'frappe.client.delete',
args: { doctype: 'Purchase Receipt', name: source_name },
callback: function(r) {
if (!r.exc) {
frappe.show_alert({
message: __(source_name + ' automatically deleted.'),
indicator: 'green'
}, 6);
frm._source_name = null;
} else {
frappe.msgprint(__('Could not delete ' + source_name + '. Please delete manually.'));
}
}
});
}
}
});
function subtract_10_minutes(time_str) {
if (!time_str) return time_str;
let parts = time_str.split(':');
let hours = parseInt(parts[0]) || 0;
let minutes = parseInt(parts[1]) || 0;
let seconds = parseInt(parts[2]) || 0;
let total_seconds = (hours * 3600) + (minutes * 60) + seconds - (10 * 60);
if (total_seconds < 0) total_seconds += 86400;

let new_hours = Math.floor(total_seconds / 3600);
let new_minutes = Math.floor((total_seconds % 3600) / 60);
let new_seconds = total_seconds % 60;

// Pad to HH:MM:SS
return String(new_hours).padStart(2, '0') + ':' +
String(new_minutes).padStart(2, '0') + ':' +
String(new_seconds).padStart(2, '0');
}

function new_purchase_receipt_with_data(frm) {

Expand All @@ -17,7 +78,7 @@ function new_purchase_receipt_with_data(frm) {
}

// Store source data BEFORE navigating away
let source = JSON.parse(JSON.stringify(frm.doc));
let source = JSON.parse(JSON.stringify(frm.doc));
let source_name = frm.doc.name;
let source_docstatus = frm.doc.docstatus;

Expand All @@ -38,7 +99,7 @@ function populate_new_form(source, source_name, source_docstatus) {
return;
}

//Helper: safe set value
// Helper: safe set value
function s(field, value) {
try {
if (new_frm.fields_dict[field] !== undefined && value) {
Expand All @@ -47,7 +108,7 @@ function populate_new_form(source, source_name, source_docstatus) {
} catch(e) {}
}

//Header
// Header
s('company', source.company);
s('supplier', source.supplier);
s('supplier_name', source.supplier_name);
Expand All @@ -70,19 +131,24 @@ function populate_new_form(source, source_name, source_docstatus) {
s('lr_no', source.lr_no);
s('lr_date', source.lr_date);
s('supplier_delivery_note', source.supplier_delivery_note);
s('posting_date', source.posting_date);

let adjusted_time = subtract_10_minutes(source.posting_time);
if (new_frm.fields_dict['posting_time'] !== undefined && adjusted_time) {
new_frm.doc['posting_time'] = adjusted_time;
new_frm.doc['set_posting_time'] = 1;
}

//Address & Contact
// Address & Contact
s('supplier_address', source.supplier_address);
s('contact_person', source.contact_person);
s('contact_email', source.contact_email);
s('shipping_address', source.shipping_address);
s('billing_address', source.billing_address);

// Store source info for after_save
new_frm._source_name = source_name;
new_frm._source_docstatus = source_docstatus;

//ITEMS
// ITEMS
new_frm.doc.items = [];

(source.items || []).forEach(function(item, idx) {
Expand Down Expand Up @@ -124,7 +190,7 @@ function populate_new_form(source, source_name, source_docstatus) {
row.allow_zero_valuation_rate = item.allow_zero_valuation_rate;
});

//TAXES
// TAXES
new_frm.doc.taxes = [];

(source.taxes || []).forEach(function(tax, idx) {
Expand All @@ -141,59 +207,15 @@ function populate_new_form(source, source_name, source_docstatus) {
row.row_id = tax.row_id;
});

//Refresh all fields
// Refresh all fields
new_frm.refresh_fields();
new_frm.refresh_field('items');
new_frm.refresh_field('taxes');

try { new_frm.script_manager.trigger('calculate_taxes_and_totals'); } catch(e) {}

frappe.show_alert({
message: __('Data carried forward from ' + source_name + '. Save to auto-cancel original.'),
message: __('Data carried forward from ' + source_name + ' | Posting Time set to ' + adjusted_time + '. Submit to auto-cancel original.'),
indicator: 'blue'
}, 5);
}

//Auto-cancel original when new receipt is SAVED
frappe.ui.form.on('Purchase Receipt', {
after_save: function(frm) {
let source_name = frm._source_name;
let source_docstatus = frm._source_docstatus;

if (!source_name) return;

if (source_docstatus === 1) {
frappe.call({
method: 'frappe.client.cancel',
args: { doctype: 'Purchase Receipt', name: source_name },
callback: function(r) {
if (!r.exc) {
frappe.show_alert({
message: __(source_name + ' automatically cancelled.'),
indicator: 'green'
}, 6);
frm._source_name = null;
} else {
frappe.msgprint(__('Could not auto-cancel ' + source_name + '. Please cancel manually.'));
}
}
});
} else if (source_docstatus === 0) {
frappe.call({
method: 'frappe.client.delete',
args: { doctype: 'Purchase Receipt', name: source_name },
callback: function(r) {
if (!r.exc) {
frappe.show_alert({
message: __('Draft' + source_name + ' automatically deleted.'),
indicator: 'green'
}, 6);
frm._source_name = null;
} else {
frappe.msgprint(__('Could not delete draft ' + source_name + '. Please delete manually.'));
}
}
});
}
}
});
}, 6);
}
37 changes: 37 additions & 0 deletions rmax_custom/public/js/purchase_receipt_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
frappe.listview_settings['Purchase Receipt'] = {
onload: function(listview) {
listview.page.add_action_item(__('Create Single Purchase Invoice'), function() {
let selected = listview.get_checked_items();
if (selected.length === 0) {
frappe.msgprint(__('Please select at least one Purchase Receipt.'));
return;
}

let receipt_names = selected.map(r => r.name);

frappe.confirm(
__('Create 1 Purchase Invoice from ' + receipt_names.length + ' Purchase Receipt(s)?'),
function() {
frappe.call({
method: 'rmax_custom.api.purchase_invoice.create_single_purchase_invoice',
args: {
receipt_names: receipt_names
},
freeze: true,
freeze_message: __('Creating Purchase Invoice...'),
callback: function(r) {
if (r.message) {
frappe.msgprint({
title: __('Success'),
message: __('Purchase Invoice <a href="/app/purchase-invoice/' + r.message + '">' + r.message + '</a> created successfully.'),
indicator: 'green'
});
listview.refresh();
}
}
});
}
);
});
}
};
Loading