Compare commits

...

22 Commits

Author SHA1 Message Date
Nabin Hait
f6c396f176 Merge branch 'hotfix' 2017-05-30 15:59:04 +05:30
Nabin Hait
93b5367485 bumped to version 8.0.41 2017-05-30 16:29:04 +06:00
Manas Solanki
5995010007 Correction in the validation message (#9079) 2017-05-30 15:56:48 +05:30
bcornwellmott
0d38a6b495 Fix grammar error in HR Training Results (#9076)
* Fix Typo in message

Issue #9074

* Added  condition for notifying user of grade.
2017-05-30 15:56:02 +05:30
Manas Solanki
a85b68ee77 link for timesheet, fix https://github.com/frappe/erpnext/issues/9042 (#9071) 2017-05-30 15:55:00 +05:30
Manas Solanki
ca31425bb4 field for the naming series should have no copy attribute (#9067) 2017-05-30 15:36:50 +05:30
Manas Solanki
253c53cf53 updating serial no status from scheduler #9061 (#9066) 2017-05-30 15:35:58 +05:30
Nabin Hait
ac3b2aa913 Set change amount automatically only if it is a cash transaction (#9065) 2017-05-30 15:35:01 +05:30
Nabin Hait
5d7e8d9e83 Minor fix in timesheet (#9063) 2017-05-30 15:34:35 +05:30
Nabin Hait
6a5cf672c1 Fixed opportunity status (#9062) 2017-05-30 15:34:20 +05:30
Manas Solanki
5f7b88d9c3 set student roll no in backend if not given (#9039) 2017-05-30 15:33:50 +05:30
Nabin Hait
465d8352aa Merge branch 'hotfix' 2017-05-26 21:35:42 +05:30
Nabin Hait
38ada81487 bumped to version 8.0.40 2017-05-26 22:05:41 +06:00
Prateeksha Singh
e38c70c8bc [fix] uppercase filter frappe/erpnext#8996 (#9026) 2017-05-26 21:34:18 +05:30
Makarand Bauskar
157c334737 [minor] added get_terms methods to erpnext.utils so that it can be used in non transactional documents (#9037) 2017-05-26 21:32:33 +05:30
Nabin Hait
4b12896941 Party Dashboard: Consider any random company if default company not set (#9046) 2017-05-26 21:25:36 +05:30
Sagar Vora
7b0c682635 [fix] add round off difference to last row in landed cost voucher (#8989)
* [fix] add round off difference to last row in landed cost voucher

* Add test case for odd numbers

* Add assertEquals to verify applicable charges

* Use make_purchase_receipt, move round off logic

* Allow rounding difference

* Specify cost center to pass test
2017-05-26 14:58:54 +05:30
Nabin Hait
d45a036b5e Merge branch 'hotfix' 2017-05-25 14:19:50 +05:30
Nabin Hait
d7d9cd2938 bumped to version 8.0.39 2017-05-25 14:49:50 +06:00
Nabin Hait
7e7dc0f254 Update party.py 2017-05-25 14:18:20 +05:30
Ayush Shukla
e9cf1aba77 added float and rebase with hotfix (#9019) 2017-05-25 14:14:55 +05:30
Manas Solanki
c2bf50042f validate email id only if provided (#9020) 2017-05-25 14:14:42 +05:30
25 changed files with 201 additions and 78 deletions

View File

@@ -2,7 +2,7 @@
from __future__ import unicode_literals
import frappe
__version__ = '8.0.38'
__version__ = '8.0.41'
def get_default_company(user=None):

View File

@@ -299,6 +299,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
this.calculate_write_off_amount()
}else {
this.frm.set_value("change_amount", 0.0)
this.frm.set_value("base_change_amount", 0.0)
}
this.frm.refresh_fields();

View File

@@ -709,7 +709,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
filter: function (item, input) {
var value = item.value.toLowerCase();
if (value.indexOf('is_action') !== -1 ||
value.indexOf(input) !== -1) {
value.indexOf(input.toLowerCase()) !== -1) {
return true;
}
},

View File

@@ -8,7 +8,7 @@ import datetime
from frappe import _, msgprint, scrub
from frappe.defaults import get_user_permissions
from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff, \
add_years, get_timestamp, nowdate
add_years, get_timestamp, nowdate, flt
from frappe.geo.doctype.address.address import get_address_display, get_default_address
from frappe.email.doctype.contact.contact import get_contact_details, get_default_contact
from erpnext.exceptions import PartyFrozen, InvalidCurrency, PartyDisabled, InvalidAccountCurrency
@@ -366,8 +366,10 @@ def get_timeline_data(doctype, name):
def get_dashboard_info(party_type, party):
current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True)
party_account_currency = get_party_account_currency(party_type, party, frappe.db.get_default("company"))
company_default_currency = get_default_currency()
company = frappe.db.get_default("company") or frappe.get_all("Company")[0].name
party_account_currency = get_party_account_currency(party_type, party, company)
company_default_currency = get_default_currency() \
or frappe.db.get_value('Company', company, 'default_currency')
if party_account_currency==company_default_currency:
total_field = "base_grand_total"
@@ -389,10 +391,10 @@ def get_dashboard_info(party_type, party):
where party_type = %s and party=%s""", (party_type, party))
info = {}
info["billing_this_year"] = billing_this_year[0][0] if billing_this_year else 0
info["billing_this_year"] = flt(billing_this_year[0][0]) if billing_this_year else 0
info["currency"] = party_account_currency
info["total_unpaid"] = total_unpaid[0][0] if total_unpaid else 0
info["total_unpaid"] = flt(total_unpaid[0][0]) if total_unpaid else 0
if party_type == "Supplier":
info["total_unpaid"] = -1 * info["total_unpaid"]
return info
return info

View File

@@ -19,10 +19,10 @@ status_map = {
["Converted", "has_customer"],
],
"Opportunity": [
["Quotation", "has_active_quotation"],
["Converted", "has_ordered_quotation"],
["Lost", "eval:self.status=='Lost'"],
["Lost", "has_lost_quotation"],
["Quotation", "has_active_quotation"],
["Converted", "has_ordered_quotation"],
["Closed", "eval:self.status=='Closed'"]
],
"Quotation": [

View File

@@ -440,16 +440,16 @@ class calculate_taxes_and_totals(object):
self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
- flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
if self.doc.doctype == "Sales Invoice":
if self.doc.doctype == "Sales Invoice":
self.doc.round_floats_in(self.doc, ["paid_amount"])
paid_amount = self.doc.paid_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
change_amount = self.doc.change_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
self.calculate_write_off_amount()
self.calculate_change_amount()
paid_amount = self.doc.paid_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
change_amount = self.doc.change_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) +
flt(change_amount), self.doc.precision("outstanding_amount"))
@@ -462,7 +462,8 @@ class calculate_taxes_and_totals(object):
if self.doc.is_pos:
for payment in self.doc.get('payments'):
payment.base_amount = flt(payment.amount * self.doc.conversion_rate)
payment.amount = flt(payment.amount)
payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
paid_amount += payment.amount
base_paid_amount += payment.base_amount
elif not self.doc.is_return:
@@ -474,7 +475,9 @@ class calculate_taxes_and_totals(object):
def calculate_change_amount(self):
self.doc.change_amount = 0.0
self.doc.base_change_amount = 0.0
if self.doc.paid_amount > self.doc.grand_total:
if self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
and any([d.type == "Cash" for d in self.doc.payments]):
self.doc.change_amount = flt(self.doc.paid_amount - self.doc.grand_total +
self.doc.write_off_amount, self.doc.precision("change_amount"))

View File

@@ -35,6 +35,7 @@ frappe.ui.form.on("Opportunity", {
var doc = frm.doc;
frm.events.enquiry_from(frm);
frm.trigger('set_contact_link');
erpnext.toggle_naming_series();
if(!doc.__islocal && doc.status!=="Lost") {
if(doc.with_items){
@@ -53,6 +54,20 @@ frappe.ui.form.on("Opportunity", {
frm.add_custom_button(__('Lost'),
cur_frm.cscript['Declare Opportunity Lost']);
}
},
if(!frm.doc.__islocal && frm.perm[0].write && frm.doc.docstatus==0) {
if(frm.doc.status==="Open") {
frm.add_custom_button(__("Close"), function() {
frm.set_value("status", "Closed");
frm.save();
});
} else {
frm.add_custom_button(__("Reopen"), function() {
frm.set_value("status", "Open");
frm.save();
});
}
}
},
@@ -122,25 +137,6 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
erpnext.toggle_naming_series();
var frm = cur_frm;
if(!doc.__islocal && frm.perm[0].write && doc.docstatus==0) {
if(frm.doc.status==="Open") {
frm.add_custom_button(__("Close"), function() {
frm.set_value("status", "Closed");
frm.save();
});
} else {
frm.add_custom_button(__("Reopen"), function() {
frm.set_value("status", "Open");
frm.save();
});
}
}
}
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
if(doc.enquiry_from == 'Lead' && doc.lead)
cur_frm.cscript.lead(doc, cdt, cdn);

View File

@@ -31,7 +31,6 @@ class Opportunity(TransactionBase):
if not self.enquiry_from:
frappe.throw(_("Opportunity From field is mandatory"))
self.set_status()
self.validate_item_details()
self.validate_uom_is_integer("uom", "qty")
self.validate_lead_cust()

View File

@@ -178,7 +178,8 @@ scheduler_events = {
"erpnext.hr.doctype.employee.employee.send_birthday_reminders",
"erpnext.projects.doctype.task.task.set_tasks_as_overdue",
"erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries",
'erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary'
"erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary",
"erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status"
]
}

View File

@@ -11,6 +11,7 @@ from erpnext.accounts.party import get_party_account
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
from erpnext.controllers.accounts_controller import AccountsController
from frappe.utils.csvutils import getlink
class InvalidExpenseApproverError(frappe.ValidationError): pass
@@ -146,7 +147,7 @@ class ExpenseClaim(AccountsController):
frappe.throw(_("Cost center is required to book an expense claim"))
if not self.payable_account:
frappe.throw(_("Please set default payable account in the employee {0}").format(self.employee))
frappe.throw(_("Please set default payable account for the company {0}").format(getlink("Company",self.company)))
if self.is_paid:
if not self.mode_of_payment:

View File

@@ -27,7 +27,8 @@ class JobApplicant(Document):
def validate(self):
self.check_email_id_is_unique()
validate_email_add(self.email_id, True)
if self.email_id:
validate_email_add(self.email_id, True)
if not self.applicant_name and self.email_id:
guess = self.email_id.split('@')[0]

View File

@@ -5,8 +5,10 @@ frappe.provide("erpnext.offer_letter");
frappe.ui.form.on("Offer Letter", {
select_terms: function(frm) {
frappe.model.get_value("Terms and Conditions", frm.doc.select_terms, "terms", function(value) {
frm.set_value("terms", value.terms);
erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function(r) {
if(!r.exc) {
me.frm.set_value("terms", r.message);
}
});
},

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:event_name",
@@ -12,6 +13,7 @@
"editable_grid": 1,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -27,7 +29,7 @@
"in_standard_filter": 0,
"label": "Event Name",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -41,6 +43,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -71,6 +74,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -99,6 +103,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -129,6 +134,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -157,6 +163,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -186,6 +193,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -216,6 +224,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -244,6 +253,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -274,6 +284,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -303,6 +314,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -331,6 +343,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -361,6 +374,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -390,6 +404,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -418,6 +433,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -447,6 +463,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -476,6 +493,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -504,6 +522,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -533,6 +552,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -562,6 +582,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -592,6 +613,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -622,6 +644,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -651,17 +674,17 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-17 16:51:35.141403",
"modified": "2017-05-29 06:13:38.411039",
"modified_by": "Administrator",
"module": "HR",
"name": "Training Event",

View File

@@ -13,7 +13,9 @@ class TrainingResult(Document):
def send_result(self):
for emp in self.employees:
message = "Thank You for attending {0}. You grade is {1}".format(self.training_event, emp.grade)
message = "Thank You for attending {0}.".format(self.training_event)
if emp.grade:
message = message + "Your grade: {0}".format(emp.grade)
frappe.sendmail(frappe.db.get_value("Employee", emp.employee, "company_email"), \
subject=_("{0} Results".format(self.training_event)), \
content=message)

View File

@@ -20,6 +20,7 @@ from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
from erpnext.stock.utils import get_bin
from frappe.utils.csvutils import getlink
class OverProductionError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass
@@ -311,7 +312,7 @@ class ProductionOrder(Document):
if timesheet and timesheet.get("time_logs"):
timesheet.save()
timesheets.append(timesheet.name)
timesheets.append(getlink("Timesheet", timesheet.name))
self.planned_end_date = self.operations[-1].planned_end_time
if timesheets:

View File

@@ -368,7 +368,8 @@ def get_events(start, end, filters=None):
conditions = get_conditions(filters)
return frappe.db.sql("""select `tabTimesheet Detail`.name as name,
`tabTimesheet Detail`.docstatus as status, `tabTimesheet Detail`.parent as parent,
from_time as start_date, hours, activity_type, project, to_time as end_date,
from_time as start_date, hours, activity_type,
`tabTimesheet Detail`.project, to_time as end_date,
CONCAT(`tabTimesheet Detail`.parent, ' (', ROUND(hours,2),' hrs)') as title
from `tabTimesheet Detail`, `tabTimesheet`
where `tabTimesheet Detail`.parent = `tabTimesheet`.name

View File

@@ -604,11 +604,18 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
calculate_change_amount: function(){
this.frm.doc.change_amount = 0.0;
if(this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return){
this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total +
this.frm.doc.write_off_amount, precision("change_amount"));
this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount - this.frm.doc.base_grand_total +
this.frm.doc.base_write_off_amount, precision("base_change_amount"));
this.frm.doc.base_change_amount = 0.0;
if(this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return) {
var payment_types = $.map(cur_frm.doc.payments, function(d) { return d.type });
if (in_list(payment_types, 'Cash')) {
this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total +
this.frm.doc.write_off_amount, precision("change_amount"));
this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount -
this.frm.doc.base_grand_total + this.frm.doc.base_write_off_amount,
precision("base_change_amount"));
}
}
},

View File

@@ -993,20 +993,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
get_terms: function() {
var me = this;
if(this.frm.doc.tc_name) {
return frappe.call({
method: 'erpnext.setup.doctype.terms_and_conditions.terms_and_conditions.get_terms_and_conditions',
args: {
template_name: this.frm.doc.tc_name,
doc: this.frm.doc
},
callback: function(r) {
if(!r.exc) {
me.frm.set_value("terms", r.message);
}
}
});
}
erpnext.utils.get_terms(this.frm.doc.tc_name, this.frm.doc, function(r) {
if(!r.exc) {
me.frm.set_value("terms", r.message);
}
});
},
taxes_and_charges: function() {

View File

@@ -104,6 +104,21 @@ $.extend(erpnext.utils, {
}
}
refresh_field(table_fieldname);
},
get_terms: function(tc_name, doc, callback) {
if(tc_name) {
return frappe.call({
method: 'erpnext.setup.doctype.terms_and_conditions.terms_and_conditions.get_terms_and_conditions',
args: {
template_name: tc_name,
doc: doc
},
callback: function(r) {
callback(r)
}
});
}
}
});

View File

@@ -14,7 +14,7 @@ class StudentGroup(Document):
self.validate_strength()
if frappe.defaults.get_defaults().student_validation_setting:
self.validate_students()
self.validate_roll_no()
self.validate_and_set_child_table_fields()
validate_duplicate_student(self.students)
def validate_mandatory_fields(self):
@@ -38,9 +38,16 @@ class StudentGroup(Document):
if not frappe.db.get_value("Student", d.student, "enabled") and d.active:
frappe.throw(_("{0} - {1} is inactive student".format(d.group_roll_number, d.student_name)))
def validate_roll_no(self):
def validate_and_set_child_table_fields(self):
roll_numbers = [d.group_roll_number for d in self.students if d.group_roll_number]
max_roll_no = max(roll_numbers) if roll_numbers else 0
roll_no_list = []
for d in self.students:
if not d.student_name:
d.student_name = frappe.db.get_value("Student", d.student, "title")
if not d.group_roll_number:
max_roll_no += 1
d.group_roll_number = max_roll_no
if d.group_roll_number in roll_no_list:
frappe.throw(_("Duplicate roll number for student {0}".format(d.student_name)))
else:

View File

@@ -5,8 +5,23 @@ from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Student Group')
from frappe.utils.make_random import get_random
class TestStudentGroup(unittest.TestCase):
pass
def test_student_roll_no(self):
doc = frappe.get_doc({
"doctype": "Student Group",
"student_group_name": "_Test Student Group R",
"group_based_on": "Activity"
}).insert()
student_list = []
while len(student_list) < 3:
s = get_random("Student")
if s not in student_list:
student_list.append(s)
doc.extend("students", [{"student":d} for d in student_list])
doc.save()
self.assertEquals(max([d.group_roll_number for d in doc.students]), 3)

View File

@@ -102,9 +102,17 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
total_item_cost += flt(d[based_on])
});
var total_charges = 0.0;
$.each(this.frm.doc.items || [], function(i, item) {
item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
total_charges += item.applicable_charges
});
if (total_charges != this.frm.doc.total_taxes_and_charges){
var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
this.frm.doc.items.slice(-1)[0].applicable_charges += diff
}
refresh_field("items");
}
},

View File

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt
from frappe.model.meta import get_field_precision
from frappe.model.document import Document
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -71,7 +72,17 @@ class LandedCostVoucher(Document):
if not total:
frappe.throw(_("Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'").format(based_on))
if self.total_taxes_and_charges != sum([flt(d.applicable_charges) for d in self.get("items")]):
total_applicable_charges = sum([flt(d.applicable_charges) for d in self.get("items")])
precision = get_field_precision(frappe.get_meta("Landed Cost Item").get_field("applicable_charges"),
currency=frappe.db.get_value("Company", self.company, "default_currency", cache=True))
diff = flt(self.total_taxes_and_charges) - flt(total_applicable_charges)
diff = flt(diff, precision)
if abs(diff) < (2.0 / (10**precision)):
self.items[-1].applicable_charges += diff
else:
frappe.throw(_("Total Applicable Charges in Purchase Receipt Items table must be same as Total Taxes and Charges"))

View File

@@ -7,7 +7,7 @@ import unittest
import frappe
from frappe.utils import flt
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records
import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records, make_purchase_receipt
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
class TestLandedCostVoucher(unittest.TestCase):
@@ -133,7 +133,29 @@ class TestLandedCostVoucher(unittest.TestCase):
set_perpetual_inventory(0)
def submit_landed_cost_voucher(receipt_document_type, receipt_document):
def test_landed_cost_voucher_for_odd_numbers (self):
set_perpetual_inventory(1)
pr = make_purchase_receipt(do_not_save=True)
pr.items[0].cost_center = "_Test Company - _TC"
for x in range(2):
pr.append("items", {
"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC",
"cost_center": "_Test Company - _TC",
"qty": 5,
"rate": 50
})
pr.submit()
lcv = submit_landed_cost_voucher("Purchase Receipt", pr.name, 123.22)
self.assertEquals(lcv.items[0].applicable_charges, 41.07)
self.assertEquals(lcv.items[2].applicable_charges, 41.08)
set_perpetual_inventory(0)
def submit_landed_cost_voucher(receipt_document_type, receipt_document, charges=50):
ref_doc = frappe.get_doc(receipt_document_type, receipt_document)
lcv = frappe.new_doc("Landed Cost Voucher")
@@ -151,7 +173,7 @@ def submit_landed_cost_voucher(receipt_document_type, receipt_document):
lcv.set("taxes", [{
"description": "Insurance Charges",
"account": "_Test Account Insurance Charges - _TC",
"amount": 50
"amount": charges
}])
lcv.insert()
@@ -159,11 +181,15 @@ def submit_landed_cost_voucher(receipt_document_type, receipt_document):
distribute_landed_cost_on_items(lcv)
lcv.submit()
return lcv
def distribute_landed_cost_on_items(lcv):
based_on = lcv.distribute_charges_based_on.lower()
total = sum([flt(d.get(based_on)) for d in lcv.get("items")])
for item in lcv.get("items"):
item.applicable_charges = flt(item.get(based_on)) * flt(lcv.total_taxes_and_charges) / flt(total)
item.applicable_charges = flt(item.applicable_charges, lcv.precision("applicable_charges", item))
test_records = frappe.get_test_records('Landed Cost Voucher')

View File

@@ -324,3 +324,12 @@ def update_serial_nos_after_submit(controller, parentfield):
update_rejected_serial_nos = False
if accepted_serial_nos_updated:
break
def update_maintenance_status():
serial_nos = frappe.db.sql('''select name from `tabSerial No` where (amc_expiry_date<%s or
warranty_expiry_date<%s) and maintenance_status not in ('Out of Warranty', 'Out of AMC')''',
(nowdate(), nowdate()))
for serial_no in serial_nos:
doc = frappe.get_doc("Serial No", serial_no[0])
doc.set_maintenance_status()
frappe.db.set_value('Serial No', doc.name, 'maintenance_status', doc.maintenance_status)