Merge branch 'rebrand-ui' of https://github.com/frappe/erpnext into desk-enhancements

This commit is contained in:
Shivam Mishra
2020-12-16 13:21:10 +05:30
311 changed files with 9144 additions and 2211 deletions

View File

@@ -58,6 +58,7 @@ class Customer(TransactionBase):
self.set_loyalty_program()
self.check_customer_group_change()
self.validate_default_bank_account()
self.validate_internal_customer()
# set loyalty program tier
if frappe.db.exists('Customer', self.name):
@@ -82,6 +83,11 @@ class Customer(TransactionBase):
if not is_company_account:
frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
def validate_internal_customer(self):
if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"):
frappe.throw(_("Internal Customer for company {0} already exists").format(
frappe.bold(self.represents_company)))
def on_update(self):
self.validate_name_with_customer_group()
self.create_primary_contact()
@@ -398,7 +404,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
# form a list of emails and names to show to the user
credit_controller_users_formatted = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users]
if not credit_controller_users_formatted:
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer)))
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.").format(customer))
message = """Please contact any of the following users to extend the credit limits for {0}:
<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users_formatted))

View File

@@ -1,5 +1,6 @@
{
"actions": [],
"allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-05-24 19:29:08",
@@ -932,7 +933,7 @@
"is_submittable": 1,
"links": [],
"max_attachments": 1,
"modified": "2020-07-26 17:46:19.951223",
"modified": "2020-10-30 13:58:59.212060",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",

View File

@@ -8,7 +8,7 @@ frappe.ui.form.on("Sales Order", {
frm.custom_make_buttons = {
'Delivery Note': 'Delivery Note',
'Pick List': 'Pick List',
'Sales Invoice': 'Invoice',
'Sales Invoice': 'Sales Invoice',
'Material Request': 'Material Request',
'Purchase Order': 'Purchase Order',
'Project': 'Project',
@@ -326,9 +326,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
callback: function(r) {
if(r.message) {
frappe.msgprint({
message: __('Work Orders Created: {0}',
[r.message.map(function(d) {
return repl('<a href="/desk/Form/Work Order/%(name)s">%(name)s</a>', {name:d})
message: __('Work Orders Created: {0}', [r.message.map(function(d) {
return repl('<a href="#Form/Work Order/%(name)s">%(name)s</a>', {name:d})
}).join(', ')]),
indicator: 'green'
})

View File

@@ -1,5 +1,6 @@
{
"actions": [],
"allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-06-18 12:39:59",
@@ -1460,7 +1461,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2020-10-07 14:30:01.782617",
"modified": "2020-10-30 13:59:18.628077",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",

View File

@@ -62,6 +62,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va
`tabItem` item {bin_join_selection}
WHERE
item.disabled = 0
AND item.is_stock_item = 1
AND item.has_variants = 0
AND item.is_sales_item = 1
AND item.is_fixed_asset = 0

View File

@@ -98,48 +98,31 @@ erpnext.PointOfSale.Controller = class {
dialog.show();
}
prepare_app_defaults(data) {
async prepare_app_defaults(data) {
this.pos_opening = data.name;
this.company = data.company;
this.pos_profile = data.pos_profile;
this.pos_opening_time = data.period_start_date;
this.item_stock_map = {};
this.settings = {};
frappe.db.get_value('Stock Settings', undefined, 'allow_negative_stock').then(({ message }) => {
this.allow_negative_stock = flt(message.allow_negative_stock) || false;
});
frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
this.customer_groups = profile.customer_groups.map(group => group.customer_group);
this.cart.make_customer_selector();
this.settings.customer_groups = profile.customer_groups.map(group => group.customer_group);
this.settings.hide_images = profile.hide_images;
this.settings.auto_add_item_to_cart = profile.auto_add_item_to_cart;
this.make_app();
});
this.item_stock_map = {};
this.make_app();
}
set_opening_entry_status() {
this.page.set_title_sub(
`<span class="indicator orange">
<a class="text-muted" href="/desk/Form/POS%20Opening%20Entry/${this.pos_opening}">
Opened at ${moment(this.pos_opening_time).format("Do MMMM, h:mma")}
</a>
</span>`);
}
make_app() {
return frappe.run_serially([
() => frappe.dom.freeze(),
() => {
this.set_opening_entry_status();
this.prepare_dom();
this.prepare_components();
this.prepare_menu();
},
() => this.make_new_invoice(),
() => frappe.dom.unfreeze(),
() => this.page.set_title(__('Point of Sale')),
]);
this.prepare_dom();
this.prepare_components();
this.prepare_menu();
this.make_new_invoice();
}
prepare_dom() {
@@ -225,12 +208,11 @@ erpnext.PointOfSale.Controller = class {
this.item_selector = new erpnext.PointOfSale.ItemSelector({
wrapper: this.$components_wrapper,
pos_profile: this.pos_profile,
settings: this.settings,
events: {
item_selected: args => this.on_cart_update(args),
get_frm: () => this.frm || {},
get_allowed_item_group: () => this.item_groups
get_frm: () => this.frm || {}
}
})
}
@@ -238,6 +220,7 @@ erpnext.PointOfSale.Controller = class {
init_item_cart() {
this.cart = new erpnext.PointOfSale.ItemCart({
wrapper: this.$components_wrapper,
settings: this.settings,
events: {
get_frm: () => this.frm,
@@ -260,9 +243,7 @@ erpnext.PointOfSale.Controller = class {
this.customer_details = details;
// will add/remove LP payment method
this.payment.render_loyalty_points_payment_mode();
},
get_allowed_customer_group: () => this.customer_groups
}
}
})
}
@@ -416,8 +397,6 @@ erpnext.PointOfSale.Controller = class {
})
}
toggle_recent_order_list(show) {
this.toggle_components(!show);
this.recent_order_list.toggle_component(show);
@@ -434,10 +413,12 @@ erpnext.PointOfSale.Controller = class {
make_new_invoice() {
return frappe.run_serially([
() => frappe.dom.freeze(),
() => this.make_sales_invoice_frm(),
() => this.set_pos_profile_data(),
() => this.set_pos_profile_status(),
() => this.cart.load_invoice(),
() => frappe.dom.unfreeze()
]);
}
@@ -494,16 +475,6 @@ erpnext.PointOfSale.Controller = class {
return this.frm.trigger("set_pos_data");
}
raise_exception_for_pos_profile() {
setTimeout(() => frappe.set_route('List', 'POS Profile'), 2000);
frappe.throw(__("POS Profile is required to use Point-of-Sale"));
}
set_invoice_status() {
const [status, indicator] = frappe.listview_settings["POS Invoice"].get_indicator(this.frm.doc);
this.page.set_indicator(status, indicator);
}
set_pos_profile_status() {
this.page.set_indicator(this.pos_profile, "blue");
}
@@ -631,8 +602,7 @@ erpnext.PointOfSale.Controller = class {
})
} else if (available_qty < qty_needed) {
frappe.show_alert({
message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.',
[bold_item_code, bold_warehouse, bold_available_qty]),
message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.', [bold_item_code, bold_warehouse, bold_available_qty]),
indicator: 'orange'
});
frappe.utils.play_sound("error");

View File

@@ -1,12 +1,14 @@
erpnext.PointOfSale.ItemCart = class {
constructor({ wrapper, events }) {
constructor({ wrapper, events, settings }) {
this.wrapper = wrapper;
this.events = events;
this.customer_info = undefined;
this.hide_images = settings.hide_images;
this.allowed_customer_groups = settings.customer_groups;
this.init_component();
}
init_component() {
this.prepare_dom();
this.init_child_components();
@@ -32,15 +34,16 @@ erpnext.PointOfSale.ItemCart = class {
`<div class="customer-section"></div>`
)
this.$customer_section = this.$component.find('.customer-section');
this.make_customer_selector();
}
reset_customer_selector() {
const frm = this.events.get_frm();
frm.set_value('customer', '');
this.make_customer_selector();
this.customer_field.set_focus();
}
init_cart_components() {
this.$component.append(
`<div class="cart-container">
@@ -54,7 +57,7 @@ erpnext.PointOfSale.ItemCart = class {
<div class="cart-items-section"></div>
<div class="cart-totals-section"></div>
<div class="numpad-section"></div>
</div>
</div>
</div>`
);
this.$cart_container = this.$component.find('.cart-container');
@@ -70,7 +73,7 @@ erpnext.PointOfSale.ItemCart = class {
this.make_no_items_placeholder();
}
make_no_items_placeholder() {
this.$cart_header.css('display', 'none');
this.$cart_items_wrapper.html(
@@ -111,7 +114,7 @@ erpnext.PointOfSale.ItemCart = class {
this.$add_discount_elem = this.$component.find(".add-discount-wrapper");
}
make_cart_numpad() {
this.$numpad_section = this.$component.find('.numpad-section');
@@ -147,7 +150,7 @@ erpnext.PointOfSale.ItemCart = class {
`<div class="numpad-btn checkout-btn" data-button-value="checkout">Checkout</div>`
)
}
bind_events() {
const me = this;
this.$customer_section.on('click', '.reset-customer-btn', function (e) {
@@ -186,7 +189,7 @@ erpnext.PointOfSale.ItemCart = class {
this.$component.on('click', '.checkout-btn', function() {
if ($(this).attr('style').indexOf('--blue-500') == -1) return;
me.events.checkout();
me.toggle_checkout_btn(false);
});
@@ -219,7 +222,7 @@ erpnext.PointOfSale.ItemCart = class {
if (btn === '.') shortcut_key = 'ctrl+>';
// to account for fieldname map
const fieldname = this.number_pad.fieldnames[btn] ? this.number_pad.fieldnames[btn] :
const fieldname = this.number_pad.fieldnames[btn] ? this.number_pad.fieldnames[btn] :
typeof btn === 'string' ? frappe.scrub(btn) : btn;
let shortcut_label = shortcut_key.split('+').map(frappe.utils.to_title_case).join('+');
@@ -230,7 +233,7 @@ erpnext.PointOfSale.ItemCart = class {
const cart_is_visible = this.$component.is(":visible");
if (cart_is_visible && this.item_is_selected && this.$numpad_section.is(":visible")) {
this.$numpad_section.find(`.numpad-btn[data-button-value="${fieldname}"]`).click();
}
}
})
}
}
@@ -268,7 +271,7 @@ erpnext.PointOfSale.ItemCart = class {
}
});
}
toggle_item_highlight(item) {
const $cart_item = $(item);
const item_is_highlighted = $cart_item.attr("style") == "background-color:var(--gray-50);";
@@ -289,7 +292,7 @@ erpnext.PointOfSale.ItemCart = class {
`);
const me = this;
const query = { query: 'erpnext.controllers.queries.customer_query' };
const allowed_customer_group = this.events.get_allowed_customer_group() || [];
const allowed_customer_group = this.allowed_customer_groups || [];
if (allowed_customer_group.length) {
query.filters = {
customer_group: ['in', allowed_customer_group]
@@ -324,7 +327,7 @@ erpnext.PointOfSale.ItemCart = class {
});
this.customer_field.toggle_label(false);
}
fetch_customer_details(customer) {
if (customer) {
return new Promise((resolve) => {
@@ -411,9 +414,9 @@ erpnext.PointOfSale.ItemCart = class {
);
}
}
update_customer_section() {
const { customer, email_id='', mobile_no='' } = this.customer_info || {};
const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
if (customer) {
this.$customer_section.html(
@@ -459,7 +462,7 @@ erpnext.PointOfSale.ItemCart = class {
return `<div class="customer-image customer-abbr">${frappe.get_abbr(customer)}</div>`
}
}
update_totals_section(frm) {
if (!frm) frm = this.events.get_frm();
@@ -469,7 +472,7 @@ erpnext.PointOfSale.ItemCart = class {
const taxes = frm.doc.taxes.map(t => { return { description: t.description, rate: t.rate }})
this.render_taxes(frm.doc.base_total_taxes_and_charges, taxes);
}
render_net_total(value) {
const currency = this.events.get_frm().doc.currency;
this.$totals_section.find('.net-total-container').html(
@@ -480,7 +483,7 @@ erpnext.PointOfSale.ItemCart = class {
`<div>Net Total: <span>${format_currency(value, currency)}</span></div>`
);
}
render_grand_total(value) {
const currency = this.events.get_frm().doc.currency;
this.$totals_section.find('.grand-total-container').html(
@@ -518,12 +521,12 @@ erpnext.PointOfSale.ItemCart = class {
const item_code_attr = `[data-item-code="${escape(item_code)}"]`;
const uom_attr = `[data-uom=${escape(uom)}]`;
const item_selector = batch_no ?
const item_selector = batch_no ?
`.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
return this.$cart_items_wrapper.find(item_selector);
}
update_item_html(item, remove_item) {
const $item = this.get_cart_item(item);
@@ -534,7 +537,7 @@ erpnext.PointOfSale.ItemCart = class {
const search_field = batch_no ? 'batch_no' : 'item_code';
const search_value = batch_no || item_code;
const item_row = this.events.get_frm().doc.items.find(i => i[search_field] === search_value && i.uom === uom);
this.render_cart_item(item_row, $item);
}
@@ -543,14 +546,14 @@ erpnext.PointOfSale.ItemCart = class {
this.update_empty_cart_section(no_of_cart_items);
}
render_cart_item(item_data, $item_to_update) {
const currency = this.events.get_frm().doc.currency;
const me = this;
if (!$item_to_update.length) {
this.$cart_items_wrapper.append(
`<div class="cart-item-wrapper"
`<div class="cart-item-wrapper"
data-item-code="${escape(item_data.item_code)}" data-uom="${escape(item_data.uom)}"
data-batch-no="${escape(item_data.batch_no || '')}">
</div>
@@ -589,7 +592,7 @@ erpnext.PointOfSale.ItemCart = class {
me.$cart_header.find(".rate-amount-header").css("width", max_width);
me.$cart_items_wrapper.find(".item-rate-amount").css("width", max_width);
}
function get_rate_discount_html() {
if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) {
return `
@@ -641,7 +644,7 @@ erpnext.PointOfSale.ItemCart = class {
const scrollTop = $item.offset().top - this.$cart_items_wrapper.offset().top + this.$cart_items_wrapper.scrollTop();
this.$cart_items_wrapper.animate({ scrollTop });
}
update_selector_value_in_cart_item(selector, value, item) {
const $item_to_update = this.get_cart_item(item);
$item_to_update.attr(`data-${selector}`, value);
@@ -670,7 +673,7 @@ erpnext.PointOfSale.ItemCart = class {
});
}
}
update_empty_cart_section(no_of_cart_items) {
const $no_item_element = this.$cart_items_wrapper.find('.no-item-wrapper');
@@ -679,7 +682,7 @@ erpnext.PointOfSale.ItemCart = class {
no_of_cart_items === 0 && !$no_item_element.length && this.make_no_items_placeholder();
}
on_numpad_event($btn) {
const current_action = $btn.attr('data-button-value');
const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action);
@@ -698,7 +701,7 @@ erpnext.PointOfSale.ItemCart = class {
this.prev_action = undefined;
}
this.numpad_value = '';
} else if (current_action === 'checkout') {
this.prev_action = undefined;
this.toggle_item_highlight();
@@ -724,7 +727,7 @@ erpnext.PointOfSale.ItemCart = class {
frappe.utils.play_sound("error");
return;
}
if (flt(this.numpad_value) > 100 && this.prev_action === 'discount_percentage') {
frappe.show_alert({
message: __('Discount cannot be greater than 100%'),
@@ -736,7 +739,7 @@ erpnext.PointOfSale.ItemCart = class {
this.events.numpad_event(this.numpad_value, this.prev_action);
}
highlight_numpad_btn($btn, curr_action) {
const curr_action_is_highlighted = $btn.hasClass('highlighted-numpad-btn');
const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
@@ -791,9 +794,7 @@ erpnext.PointOfSale.ItemCart = class {
this.$cart_container.css('display', 'none');
this.$customer_section.css({
'height': '100%',
'padding-top': '0px',
'overflow-x': 'hidden',
'overflow-y': 'scroll'
'padding-top': '0px'
});
this.$customer_section.find('.customer-details').html(
`<div class="header">
@@ -829,9 +830,7 @@ erpnext.PointOfSale.ItemCart = class {
this.$cart_container.css('display', 'flex');
this.$customer_section.css({
'height': '',
'padding-top': '',
'overflow-x': '',
'overflow-y': ''
'padding-top': ''
});
this.update_customer_section();
@@ -905,7 +904,7 @@ erpnext.PointOfSale.ItemCart = class {
}
fetch_customer_transactions() {
frappe.db.get_list('POS Invoice', {
frappe.db.get_list('POS Invoice', {
filters: { customer: this.customer_info.customer, docstatus: 1 },
fields: ['name', 'grand_total', 'status', 'posting_date', 'posting_time', 'currency'],
limit: 20
@@ -960,7 +959,7 @@ erpnext.PointOfSale.ItemCart = class {
this.events.customer_details_updated(this.customer_info);
this.update_customer_section();
})
this.$cart_items_wrapper.html('');
if (frm.doc.items.length) {
frm.doc.items.forEach(item => {
@@ -987,5 +986,5 @@ erpnext.PointOfSale.ItemCart = class {
toggle_component(show) {
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
}
}

View File

@@ -1,14 +1,16 @@
import onScan from 'onscan.js';
erpnext.PointOfSale.ItemSelector = class {
constructor({ frm, wrapper, events, pos_profile }) {
constructor({ frm, wrapper, events, pos_profile, settings }) {
this.wrapper = wrapper;
this.events = events;
this.pos_profile = pos_profile;
this.hide_images = settings.hide_images;
this.auto_add_item = settings.auto_add_item_to_cart;
this.inti_component();
}
inti_component() {
this.prepare_dom();
this.make_search_bar();
@@ -28,8 +30,9 @@ erpnext.PointOfSale.ItemSelector = class {
<div class="items-container"></div>
</section>`
);
this.$component = this.wrapper.find('.items-selector');
this.$items_container = this.$component.find('.items-container');
}
async load_items_data() {
@@ -53,7 +56,7 @@ erpnext.PointOfSale.ItemSelector = class {
let { item_group, pos_profile } = this;
!item_group && (item_group = this.parent_item_group);
return frappe.call({
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
freeze: true,
@@ -63,7 +66,6 @@ erpnext.PointOfSale.ItemSelector = class {
render_item_list(items) {
this.$items_container = this.$component.find('.items-container');
this.$items_container.html('');
items.forEach(item => {
@@ -73,14 +75,15 @@ erpnext.PointOfSale.ItemSelector = class {
}
get_item_html(item) {
const me = this;
const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange";
function get_item_image_html() {
if (item_image) {
return `<div class="item-display">
<img src="${item_image}" alt="${frappe.get_abbr(item.item_name)}">
</div>`;
if (!me.hide_images && item_image) {
return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
<img class="h-full" src="${item_image}" alt="${frappe.get_abbr(item.item_name)}" style="object-fit: cover;">
</div>`
} else {
return `<div class="item-display abbr">${frappe.get_abbr(item.item_name)}</div>`;
}
@@ -166,7 +169,7 @@ erpnext.PointOfSale.ItemSelector = class {
let batch_no = unescape($item.attr('data-batch-no'));
let serial_no = unescape($item.attr('data-serial-no'));
let uom = unescape($item.attr('data-uom'));
// escape(undefined) returns "undefined" then unescape returns "undefined"
batch_no = batch_no === "undefined" ? undefined : batch_no;
serial_no = serial_no === "undefined" ? undefined : serial_no;
@@ -204,6 +207,7 @@ erpnext.PointOfSale.ItemSelector = class {
ignore_inputs: true,
page: cur_page.page.page
});
// for selecting the last filtered item on search
frappe.ui.keys.on("enter", () => {
const selector_is_visible = this.$component.is(':visible');
@@ -225,7 +229,7 @@ erpnext.PointOfSale.ItemSelector = class {
}
});
}
filter_items({ search_term='' }={}) {
if (search_term) {
search_term = search_term.toLowerCase();
@@ -236,6 +240,7 @@ erpnext.PointOfSale.ItemSelector = class {
const items = this.search_index[search_term];
this.items = items;
this.render_item_list(items);
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
return;
}
}
@@ -248,15 +253,20 @@ erpnext.PointOfSale.ItemSelector = class {
}
this.items = items;
this.render_item_list(items);
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
});
}
add_filtered_item_to_cart() {
this.$items_container.find(".item-wrapper").click();
}
resize_selector(minimize) {
minimize ?
minimize ?
this.$component.find('.filter-section').css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') :
this.$component.find('.filter-section').css('grid-template-columns', 'repeat(12, minmax(0, 1fr))');
minimize ?
minimize ?
this.$component.find('.search-field').css('margin', 'var(--margin-sm) 0px') :
this.$component.find('.search-field').css('margin', '0px var(--margin-sm)');

View File

@@ -299,12 +299,12 @@ erpnext.PointOfSale.PastOrderSummary = class {
}
load_summary_of(doc, after_submission=false) {
this.toggle_summary_placeholder(false)
after_submission ?
this.$summary_wrapper.css('grid-column', 'span 10 / span 10') :
this.$summary_wrapper.css('grid-column', 'span 6 / span 6')
this.$component.css('grid-column', 'span 10 / span 10') :
this.$component.css('grid-column', 'span 6 / span 6')
this.toggle_summary_placeholder(false)
this.doc = doc;
this.attach_document_info(doc);
@@ -333,9 +333,25 @@ erpnext.PointOfSale.PastOrderSummary = class {
doc.items.forEach((item, i) => {
const item_dom = this.get_item_html(doc, item);
this.$items_container.append(item_dom);
this.set_dynamic_rate_header_width();
});
}
set_dynamic_rate_header_width() {
const rate_cols = Array.from(this.$items_container.find(".item-rate-disc"));
this.$items_container.find(".item-rate-disc").css("width", "");
let max_width = rate_cols.reduce((max_width, elm) => {
if ($(elm).width() > max_width)
max_width = $(elm).width();
return max_width;
}, 0);
max_width += 1;
if (max_width == 1) max_width = "";
this.$items_container.find(".item-rate-disc").css("width", max_width);
}
attach_payments_info(doc) {
this.$payment_container.html('');
doc.payments.forEach(p => {

View File

@@ -10,8 +10,8 @@ from frappe.utils.nestedset import get_descendants_of
def execute(filters=None):
filters = frappe._dict(filters or {})
if filters.from_date > filters.to_date:
frappe.throw(_('From Date cannot be greater than To Date'))
frappe.throw(_("From Date cannot be greater than To Date"))
columns = get_columns(filters)
data = get_data(filters)
@@ -148,14 +148,16 @@ def get_data(filters):
company_list.append(filters.get("company"))
customer_details = get_customer_details()
item_details = get_item_details()
sales_order_records = get_sales_order_details(company_list, filters)
for record in sales_order_records:
customer_record = customer_details.get(record.customer)
item_record = item_details.get(record.item_code)
row = {
"item_code": record.item_code,
"item_name": record.item_name,
"item_group": record.item_group,
"item_name": item_record.item_name,
"item_group": item_record.item_group,
"description": record.description,
"quantity": record.qty,
"uom": record.uom,
@@ -196,8 +198,8 @@ def get_conditions(filters):
return conditions
def get_customer_details():
details = frappe.get_all('Customer',
fields=['name', 'customer_name', "customer_group"])
details = frappe.get_all("Customer",
fields=["name", "customer_name", "customer_group"])
customer_details = {}
for d in details:
customer_details.setdefault(d.name, frappe._dict({
@@ -206,15 +208,25 @@ def get_customer_details():
}))
return customer_details
def get_item_details():
details = frappe.db.get_all("Item",
fields=["item_code", "item_name", "item_group"])
item_details = {}
for d in details:
item_details.setdefault(d.item_code, frappe._dict({
"item_name": d.item_name,
"item_group": d.item_group
}))
return item_details
def get_sales_order_details(company_list, filters):
conditions = get_conditions(filters)
return frappe.db.sql("""
SELECT
so_item.item_code, so_item.item_name, so_item.item_group,
so_item.description, so_item.qty, so_item.uom,
so_item.base_rate, so_item.base_amount, so.name,
so.transaction_date, so.customer, so.territory,
so_item.item_code, so_item.description, so_item.qty,
so_item.uom, so_item.base_rate, so_item.base_amount,
so.name, so.transaction_date, so.customer,so.territory,
so.project, so_item.delivered_qty,
so_item.billed_amt, so.company
FROM

View File

@@ -42,16 +42,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
me.frm.set_query('customer_address', erpnext.queries.address_query);
me.frm.set_query('shipping_address_name', erpnext.queries.address_query);
if(this.frm.fields_dict.taxes_and_charges) {
this.frm.set_query("taxes_and_charges", function() {
return {
filters: [
['Sales Taxes and Charges Template', 'company', '=', me.frm.doc.company],
['Sales Taxes and Charges Template', 'docstatus', '!=', 2]
]
}
});
}
if(this.frm.fields_dict.selling_price_list) {
this.frm.set_query("selling_price_list", function() {
@@ -479,7 +469,7 @@ frappe.ui.form.on(cur_frm.doctype,"project", function(frm) {
$.each(frm.doc["items"] || [], function(i, row) {
if(r.message) {
frappe.model.set_value(row.doctype, row.name, "cost_center", r.message);
frappe.msgprint(__("Cost Center For Item with Item Code '"+row.item_name+"' has been Changed to "+ r.message));
frappe.msgprint(__("Cost Center For Item with Item Code {0} has been Changed to {1}", [row.item_name, r.message]));
}
})
}