Compare commits
142 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc41ce95fc | ||
|
|
4948d336c4 | ||
|
|
be2527d93d | ||
|
|
7f5bb1c8aa | ||
|
|
a8d40e4409 | ||
|
|
e9b4686fec | ||
|
|
5086ef2499 | ||
|
|
1caca80203 | ||
|
|
9114c26857 | ||
|
|
941a965af4 | ||
|
|
4acd431b92 | ||
|
|
fad0d566f9 | ||
|
|
e2d4079363 | ||
|
|
b9ce1f590b | ||
|
|
36463ed790 | ||
|
|
3c8838816d | ||
|
|
11e50a8eee | ||
|
|
9de4c60bd6 | ||
|
|
5288bdeabb | ||
|
|
f5804438bb | ||
|
|
6e06357dc5 | ||
|
|
28913b97b0 | ||
|
|
c59cd46391 | ||
|
|
dda239fd49 | ||
|
|
b86a6ce26a | ||
|
|
ee212e7bb5 | ||
|
|
4952c7b3b5 | ||
|
|
4e61536f50 | ||
|
|
f2886f152b | ||
|
|
b5c56f6cea | ||
|
|
80e95388f5 | ||
|
|
daed0b655a | ||
|
|
fbb994c731 | ||
|
|
10b155a486 | ||
|
|
ce6eda709b | ||
|
|
14e1d20df3 | ||
|
|
da8a02d56e | ||
|
|
16343683d9 | ||
|
|
2bdc017aff | ||
|
|
16e05c321c | ||
|
|
23d7919865 | ||
|
|
38265efc39 | ||
|
|
2d414706dc | ||
|
|
d85e4b0d46 | ||
|
|
45d03af8f6 | ||
|
|
73a3a2a131 | ||
|
|
73804580d4 | ||
|
|
259f9779db | ||
|
|
14e0e58a7d | ||
|
|
5eb373f7b5 | ||
|
|
4595c30a7b | ||
|
|
fc2dd44694 | ||
|
|
053c54017e | ||
|
|
af473d78f2 | ||
|
|
26d096aa87 | ||
|
|
476c613ba6 | ||
|
|
2348a5f592 | ||
|
|
9d8d045c66 | ||
|
|
63914dd55b | ||
|
|
93a8042f08 | ||
|
|
b2f32dac1b | ||
|
|
577a3acaac | ||
|
|
5e46ce8a50 | ||
|
|
3b7342b7b5 | ||
|
|
531077e504 | ||
|
|
147918ed66 | ||
|
|
9bbfca9226 | ||
|
|
f1a07ff105 | ||
|
|
fb6e434315 | ||
|
|
3811d96feb | ||
|
|
1017615d02 | ||
|
|
056d627f46 | ||
|
|
663bfeacf4 | ||
|
|
e918ebd721 | ||
|
|
0dc18f0102 | ||
|
|
8ddc882a66 | ||
|
|
49365d0982 | ||
|
|
50c29c7d0f | ||
|
|
bf492122f8 | ||
|
|
38d0ed9f3a | ||
|
|
248a65b37d | ||
|
|
78f86e9385 | ||
|
|
d9f3e0c275 | ||
|
|
c40451ee2e | ||
|
|
6644406185 | ||
|
|
b3a962e121 | ||
|
|
8a28ccfa2f | ||
|
|
0a75fa09ef | ||
|
|
17a16eeaf4 | ||
|
|
3d3f0bcf54 | ||
|
|
bc8b20ae3c | ||
|
|
daf344e5fd | ||
|
|
7916792f99 | ||
|
|
27e37e68b2 | ||
|
|
a538f8a24a | ||
|
|
70ec88b733 | ||
|
|
4f0e5db216 | ||
|
|
79ed124939 | ||
|
|
c0a3cd603b | ||
|
|
7c6f990cf9 | ||
|
|
7820b171d3 | ||
|
|
0cf4cc283c | ||
|
|
9a4b173b88 | ||
|
|
6d83454237 | ||
|
|
ff231b5e62 | ||
|
|
4d74216147 | ||
|
|
b7e5ad0a31 | ||
|
|
e435592d64 | ||
|
|
7ddde8dc3a | ||
|
|
fce2881de6 | ||
|
|
e96e83d557 | ||
|
|
1b2944e871 | ||
|
|
074e73a0dd | ||
|
|
8923801881 | ||
|
|
cfafe93391 | ||
|
|
6c48ef781b | ||
|
|
adeb976a1b | ||
|
|
bb19b91ef9 | ||
|
|
bfa7f171bd | ||
|
|
b96c014daf | ||
|
|
e0c83e22d9 | ||
|
|
099ad0f5e1 | ||
|
|
cf9746dd84 | ||
|
|
b70712dbba | ||
|
|
4c057fe693 | ||
|
|
ee8ff51d60 | ||
|
|
9974b16c32 | ||
|
|
9e5f319d80 | ||
|
|
4f614b4030 | ||
|
|
3d458e973e | ||
|
|
3a19a71262 | ||
|
|
29d1a1c593 | ||
|
|
3b90de558f | ||
|
|
af7e31acb3 | ||
|
|
a2c9d35efb | ||
|
|
4c058f4056 | ||
|
|
ad67b84d43 | ||
|
|
4e81e4065b | ||
|
|
81332789cb | ||
|
|
82d7c0c9eb | ||
|
|
d60235e239 | ||
|
|
9b50b0a762 |
@@ -14,6 +14,8 @@ install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get purge -y mysql-common
|
||||
- sudo apt-get install mariadb-server mariadb-common libmariadbclient-dev
|
||||
- ./ci/fix-mariadb.sh
|
||||
|
||||
- wget http://downloads.sourceforge.net/project/wkhtmltopdf/0.12.1/wkhtmltox-0.12.1_linux-precise-amd64.deb
|
||||
- sudo dpkg -i wkhtmltox-0.12.1_linux-precise-amd64.deb
|
||||
- CFLAGS=-O0 pip install git+https://github.com/frappe/frappe.git@develop
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Includes Accounting, Inventory, CRM, Sales, Purchase, Projects, HRMS. Built on Python / MariaDB.
|
||||
|
||||
ERPNext is built on [frappe](https://github.com/frappe/frappe)
|
||||
ERPNext is built on [frappe](https://github.com/frappe/frappe) Python Framework.
|
||||
|
||||
- [User Guide](http://erpnext.org/user-guide.html)
|
||||
- [Getting Help](http://erpnext.org/getting-help.html)
|
||||
@@ -21,7 +21,7 @@ Use the bench, https://github.com/frappe/bench
|
||||
|
||||
1. go to "/login"
|
||||
1. Administrator user name: "Administrator"
|
||||
1. Administrator passowrd "admin"
|
||||
1. Administrator password: "admin"
|
||||
|
||||
### Download and Install
|
||||
|
||||
|
||||
11
ci/fix-mariadb.sh
Executable file
11
ci/fix-mariadb.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# stolen from http://cgit.drupalcode.org/octopus/commit/?id=db4f837
|
||||
includedir=`mysql_config --variable=pkgincludedir`
|
||||
thiscwd=`pwd`
|
||||
_THIS_DB_VERSION=`mysql -V 2>&1 | tr -d "\n" | cut -d" " -f6 | awk '{ print $1}' | cut -d"-" -f1 | awk '{ print $1}' | sed "s/[\,']//g"`
|
||||
if [ "$_THIS_DB_VERSION" = "5.5.40" ] && [ ! -e "$includedir-$_THIS_DB_VERSION-fixed.log" ] ; then
|
||||
cd $includedir
|
||||
sudo patch -p1 < $thiscwd/ci/my_config.h.patch &> /dev/null
|
||||
sudo touch $includedir-$_THIS_DB_VERSION-fixed.log
|
||||
fi
|
||||
22
ci/my_config.h.patch
Normal file
22
ci/my_config.h.patch
Normal file
@@ -0,0 +1,22 @@
|
||||
diff -burp a/my_config.h b/my_config.h
|
||||
--- a/my_config.h 2014-10-09 19:32:46.000000000 -0400
|
||||
+++ b/my_config.h 2014-10-09 19:35:12.000000000 -0400
|
||||
@@ -641,17 +641,4 @@
|
||||
#define SIZEOF_TIME_T 8
|
||||
/* #undef TIME_T_UNSIGNED */
|
||||
|
||||
-/*
|
||||
- stat structure (from <sys/stat.h>) is conditionally defined
|
||||
- to have different layout and size depending on the defined macros.
|
||||
- The correct macro is defined in my_config.h, which means it MUST be
|
||||
- included first (or at least before <features.h> - so, practically,
|
||||
- before including any system headers).
|
||||
-
|
||||
- __GLIBC__ is defined in <features.h>
|
||||
-*/
|
||||
-#ifdef __GLIBC__
|
||||
-#error <my_config.h> MUST be included first!
|
||||
-#endif
|
||||
-
|
||||
#endif
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '4.5.0'
|
||||
__version__ = '4.11.0'
|
||||
|
||||
@@ -124,6 +124,10 @@ def update_outstanding_amt(account, against_voucher_type, against_voucher, on_ca
|
||||
from `tabGL Entry` where voucher_type = 'Journal Voucher' and voucher_no = %s
|
||||
and account = %s and ifnull(against_voucher, '') = ''""",
|
||||
(against_voucher, account))[0][0])
|
||||
if not against_voucher_amount:
|
||||
frappe.throw(_("Against Journal Voucher {0} is already adjusted against some other voucher")
|
||||
.format(against_voucher))
|
||||
|
||||
bal = against_voucher_amount + bal
|
||||
if against_voucher_amount < 0:
|
||||
bal = -bal
|
||||
|
||||
@@ -213,10 +213,11 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
||||
return;
|
||||
|
||||
var update_jv_details = function(doc, r) {
|
||||
$.each(r.message, function(i, d) {
|
||||
var jvdetail = frappe.model.add_child(doc, "Journal Voucher Detail", "entries");
|
||||
jvdetail.account = d.account;
|
||||
jvdetail.balance = d.balance;
|
||||
var jvdetail = frappe.model.add_child(doc, "Journal Voucher Detail", "entries");
|
||||
$.each(r, function(i, d) {
|
||||
var row = frappe.model.add_child(doc, "Journal Voucher Detail", "entries");
|
||||
row.account = d.account;
|
||||
row.balance = d.balance;
|
||||
});
|
||||
refresh_field("entries");
|
||||
}
|
||||
@@ -231,7 +232,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
update_jv_details(doc, r);
|
||||
update_jv_details(doc, [r.message]);
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -245,7 +246,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
|
||||
callback: function(r) {
|
||||
frappe.model.clear_table(doc, "entries");
|
||||
if(r.message) {
|
||||
update_jv_details(doc, r);
|
||||
update_jv_details(doc, r.message);
|
||||
}
|
||||
cur_frm.set_value("is_opening", "Yes")
|
||||
}
|
||||
|
||||
@@ -76,26 +76,36 @@ class JournalVoucher(AccountsController):
|
||||
|
||||
def validate_entries_for_advance(self):
|
||||
for d in self.get('entries'):
|
||||
if not d.is_advance and not d.against_voucher and \
|
||||
not d.against_invoice and not d.against_jv:
|
||||
if not (d.against_voucher and d.against_invoice and d.against_jv):
|
||||
master_type = frappe.db.get_value("Account", d.account, "master_type")
|
||||
if (master_type == 'Customer' and flt(d.credit) > 0) or \
|
||||
(master_type == 'Supplier' and flt(d.debit) > 0):
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this \
|
||||
is an advance entry.").format(d.idx, d.account))
|
||||
if not d.is_advance:
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account))
|
||||
elif (d.against_sales_order or d.against_purchase_order) and d.is_advance != "Yes":
|
||||
frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
|
||||
|
||||
def validate_against_jv(self):
|
||||
for d in self.get('entries'):
|
||||
if d.against_jv:
|
||||
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
|
||||
if account_root_type == "Asset" and flt(d.debit) > 0:
|
||||
frappe.throw(_("For {0}, only credit entries can be linked against another debit entry")
|
||||
.format(d.account))
|
||||
elif account_root_type == "Liability" and flt(d.credit) > 0:
|
||||
frappe.throw(_("For {0}, only debit entries can be linked against another credit entry")
|
||||
.format(d.account))
|
||||
|
||||
if d.against_jv == self.name:
|
||||
frappe.throw(_("You can not enter current voucher in 'Against Journal Voucher' column"))
|
||||
|
||||
against_entries = frappe.db.sql("""select * from `tabJournal Voucher Detail`
|
||||
where account = %s and docstatus = 1 and parent = %s
|
||||
and ifnull(against_jv, '') = ''""", (d.account, d.against_jv), as_dict=True)
|
||||
and ifnull(against_jv, '') = '' and ifnull(against_invoice, '') = ''
|
||||
and ifnull(against_voucher, '') = ''""", (d.account, d.against_jv), as_dict=True)
|
||||
|
||||
if not against_entries:
|
||||
frappe.throw(_("Journal Voucher {0} does not have account {1} or already matched")
|
||||
frappe.throw(_("Journal Voucher {0} does not have account {1} or already matched against other voucher")
|
||||
.format(d.against_jv, d.account))
|
||||
else:
|
||||
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
||||
@@ -152,7 +162,7 @@ class JournalVoucher(AccountsController):
|
||||
and voucher_account != d.account:
|
||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} account") \
|
||||
.format(d.idx, d.account, doctype, field_dict.get(doctype)))
|
||||
|
||||
|
||||
if against_field in ["against_sales_order", "against_purchase_order"]:
|
||||
if voucher_account != account_master_name:
|
||||
frappe.throw(_("Row {0}: Account {1} does not match with {2} {3} Name") \
|
||||
@@ -164,7 +174,7 @@ class JournalVoucher(AccountsController):
|
||||
|
||||
def validate_against_invoice_fields(self, doctype, payment_against_voucher):
|
||||
for voucher_no, payment_list in payment_against_voucher.items():
|
||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||
["docstatus", "outstanding_amount"])
|
||||
|
||||
if voucher_properties[0] != 1:
|
||||
@@ -176,8 +186,8 @@ class JournalVoucher(AccountsController):
|
||||
|
||||
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
||||
for voucher_no, payment_list in payment_against_voucher.items():
|
||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||
["docstatus", "per_billed", "advance_paid", "grand_total"])
|
||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||
["docstatus", "per_billed", "status", "advance_paid", "grand_total"])
|
||||
|
||||
if voucher_properties[0] != 1:
|
||||
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
||||
@@ -185,7 +195,10 @@ class JournalVoucher(AccountsController):
|
||||
if flt(voucher_properties[1]) >= 100:
|
||||
frappe.throw(_("{0} {1} is fully billed").format(doctype, voucher_no))
|
||||
|
||||
if flt(voucher_properties[3]) < flt(voucher_properties[2]) + flt(sum(payment_list)):
|
||||
if cstr(voucher_properties[2]) == "Stopped":
|
||||
frappe.throw(_("{0} {1} is stopped").format(doctype, voucher_no))
|
||||
|
||||
if flt(voucher_properties[4]) < flt(voucher_properties[3]) + flt(sum(payment_list)):
|
||||
frappe.throw(_("Advance paid against {0} {1} cannot be greater \
|
||||
than Grand Total {2}").format(doctype, voucher_no, voucher_properties[3]))
|
||||
|
||||
@@ -276,26 +289,25 @@ class JournalVoucher(AccountsController):
|
||||
|
||||
def set_print_format_fields(self):
|
||||
for d in self.get('entries'):
|
||||
result = frappe.db.get_value("Account", d.account,
|
||||
["account_type", "master_type"])
|
||||
acc = frappe.db.get_value("Account", d.account, ["account_type", "master_type"], as_dict=1)
|
||||
|
||||
if not result:
|
||||
continue
|
||||
if not acc: continue
|
||||
|
||||
account_type, master_type = result
|
||||
|
||||
if master_type in ['Supplier', 'Customer']:
|
||||
if acc.master_type in ['Supplier', 'Customer']:
|
||||
if not self.pay_to_recd_from:
|
||||
self.pay_to_recd_from = frappe.db.get_value(master_type,
|
||||
' - '.join(d.account.split(' - ')[:-1]),
|
||||
master_type == 'Customer' and 'customer_name' or 'supplier_name')
|
||||
self.pay_to_recd_from = frappe.db.get_value(acc.master_type, ' - '.join(d.account.split(' - ')[:-1]),
|
||||
acc.master_type == 'Customer' and 'customer_name' or 'supplier_name')
|
||||
if self.voucher_type in ["Credit Note", "Debit Note"]:
|
||||
self.set_total_amount(d.debit or d.credit)
|
||||
|
||||
if account_type in ['Bank', 'Cash']:
|
||||
company_currency = get_company_currency(self.company)
|
||||
amt = flt(d.debit) and d.debit or d.credit
|
||||
self.total_amount = fmt_money(amt, currency=company_currency)
|
||||
from frappe.utils import money_in_words
|
||||
self.total_amount_in_words = money_in_words(amt, company_currency)
|
||||
if acc.account_type in ['Bank', 'Cash']:
|
||||
self.set_total_amount(d.debit or d.credit)
|
||||
|
||||
def set_total_amount(self, amt):
|
||||
company_currency = get_company_currency(self.company)
|
||||
self.total_amount = fmt_money(amt, currency=company_currency)
|
||||
from frappe.utils import money_in_words
|
||||
self.total_amount_in_words = money_in_words(amt, company_currency)
|
||||
|
||||
def check_credit_days(self):
|
||||
date_diff = 0
|
||||
@@ -373,7 +385,7 @@ class JournalVoucher(AccountsController):
|
||||
for d in self.get("entries"):
|
||||
master_type, master_name = frappe.db.get_value("Account", d.account,
|
||||
["master_type", "master_name"])
|
||||
if master_type == "Customer" and master_name:
|
||||
if master_type == "Customer" and master_name and flt(d.debit) > 0:
|
||||
super(JournalVoucher, self).check_credit_limit(d.account)
|
||||
|
||||
def get_balance(self):
|
||||
@@ -528,9 +540,10 @@ def get_against_sales_invoice(doctype, txt, searchfield, start, page_len, filter
|
||||
(filters["account"], "%%%s%%" % txt, start, page_len))
|
||||
|
||||
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
|
||||
from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jv_detail
|
||||
where jv_detail.parent = jv.name and jv_detail.account = %s and jv.docstatus = 1
|
||||
return frappe.db.sql("""select distinct jv.name, jv.posting_date, jv.user_remark
|
||||
from `tabJournal Voucher` jv, `tabJournal Voucher Detail` jvd
|
||||
where jvd.parent = jv.name and jvd.account = %s and jv.docstatus = 1
|
||||
and (ifnull(jvd.against_invoice, '') = '' and ifnull(jvd.against_voucher, '') = '' and ifnull(jvd.against_jv, '') = '' )
|
||||
and jv.%s like %s order by jv.name desc limit %s, %s""" %
|
||||
("%s", searchfield, "%s", "%s", "%s"),
|
||||
(filters["account"], "%%%s%%" % txt, start, page_len))
|
||||
|
||||
@@ -19,9 +19,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
this.frm.set_query('bank_cash_account', function() {
|
||||
if(!me.frm.doc.company) {
|
||||
msgprint(__("Please select company first"));
|
||||
@@ -35,12 +35,8 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
var help_content = '<i class="icon-hand-right"></i> ' + __("Note") + ':<br>'+
|
||||
'<ul>' + __("If you are unable to match the exact amount, then amend your Journal Voucher and split rows such that payment amount match the invoice amount.") + '</ul>';
|
||||
this.frm.set_value("reconcile_help", help_content);
|
||||
},
|
||||
|
||||
|
||||
get_unreconciled_entries: function() {
|
||||
var me = this;
|
||||
return this.frm.call({
|
||||
@@ -48,12 +44,12 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
method: 'get_unreconciled_entries',
|
||||
callback: function(r, rt) {
|
||||
var invoices = [];
|
||||
|
||||
|
||||
$.each(me.frm.doc.payment_reconciliation_invoices || [], function(i, row) {
|
||||
if (row.invoice_number && !inList(invoices, row.invoice_number))
|
||||
if (row.invoice_number && !inList(invoices, row.invoice_number))
|
||||
invoices.push(row.invoice_number);
|
||||
});
|
||||
|
||||
|
||||
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
|
||||
me.frm.doc.name).options = invoices.join("\n");
|
||||
|
||||
@@ -79,4 +75,4 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
|
||||
|
||||
cur_frm.add_fetch('party_account', 'master_type', 'party_type')
|
||||
cur_frm.add_fetch('party_account', 'master_type', 'party_type')
|
||||
|
||||
@@ -118,19 +118,12 @@
|
||||
"options": "Payment Reconciliation Invoice",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reconcile_help",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
"icon": "icon-resize-horizontal",
|
||||
"issingle": 1,
|
||||
"modified": "2014-07-31 05:43:03.410832",
|
||||
"modified": "2014-10-16 17:51:44.367107",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation",
|
||||
|
||||
@@ -96,13 +96,14 @@ class PaymentReconciliation(Document):
|
||||
|
||||
payment_amount = payment_amount[0][0] if payment_amount else 0
|
||||
|
||||
if d.invoice_amount > payment_amount:
|
||||
if d.invoice_amount - payment_amount > 0.005:
|
||||
non_reconciled_invoices.append({
|
||||
'voucher_no': d.voucher_no,
|
||||
'voucher_type': d.voucher_type,
|
||||
'posting_date': d.posting_date,
|
||||
'invoice_amount': flt(d.invoice_amount),
|
||||
'outstanding_amount': d.invoice_amount - payment_amount})
|
||||
'outstanding_amount': flt(d.invoice_amount - payment_amount, 2)
|
||||
})
|
||||
|
||||
self.add_invoice_entries(non_reconciled_invoices)
|
||||
|
||||
@@ -124,7 +125,7 @@ class PaymentReconciliation(Document):
|
||||
dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
|
||||
lst = []
|
||||
for e in self.get('payment_reconciliation_payments'):
|
||||
if e.invoice_type and e.invoice_number:
|
||||
if e.invoice_type and e.invoice_number and e.allocated_amount:
|
||||
lst.append({
|
||||
'voucher_no' : e.journal_voucher,
|
||||
'voucher_detail_no' : e.voucher_detail_number,
|
||||
@@ -134,7 +135,7 @@ class PaymentReconciliation(Document):
|
||||
'is_advance' : e.is_advance,
|
||||
'dr_or_cr' : dr_or_cr,
|
||||
'unadjusted_amt' : flt(e.amount),
|
||||
'allocated_amt' : flt(e.amount)
|
||||
'allocated_amt' : flt(e.allocated_amount)
|
||||
})
|
||||
|
||||
if lst:
|
||||
@@ -162,18 +163,23 @@ class PaymentReconciliation(Document):
|
||||
|
||||
invoices_to_reconcile = []
|
||||
for p in self.get("payment_reconciliation_payments"):
|
||||
if p.invoice_type and p.invoice_number:
|
||||
if p.invoice_type and p.invoice_number and p.allocated_amount:
|
||||
invoices_to_reconcile.append(p.invoice_number)
|
||||
|
||||
if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}):
|
||||
frappe.throw(_("{0}: {1} not found in Invoice Details table")
|
||||
.format(p.invoice_type, p.invoice_number))
|
||||
|
||||
if p.amount > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number):
|
||||
frappe.throw(_("Row {0}: Payment amount must be less than or equals to invoice outstanding amount. Please refer Note below.").format(p.idx))
|
||||
if flt(p.allocated_amount) > flt(p.amount):
|
||||
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to JV amount {2}")
|
||||
.format(p.idx, p.allocated_amount, p.amount))
|
||||
|
||||
if flt(p.allocated_amount) > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number):
|
||||
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2}")
|
||||
.format(p.idx, p.allocated_amount, unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number)))
|
||||
|
||||
if not invoices_to_reconcile:
|
||||
frappe.throw(_("Please select Invoice Type and Invoice Number in atleast one row"))
|
||||
frappe.throw(_("Please select Allocated Amount, Invoice Type and Invoice Number in atleast one row"))
|
||||
|
||||
def check_condition(self, dr_or_cr):
|
||||
cond = self.from_date and " and posting_date >= '" + self.from_date + "'" or ""
|
||||
|
||||
@@ -53,11 +53,20 @@
|
||||
"label": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "allocated_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Allocated amount",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Sales Invoice",
|
||||
"fieldname": "invoice_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_list_view": 0,
|
||||
"label": "Invoice Type",
|
||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Voucher",
|
||||
"permlevel": 0,
|
||||
@@ -95,7 +104,7 @@
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2014-07-21 16:53:56.206169",
|
||||
"modified": "2014-10-16 17:40:54.040194",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation Payment",
|
||||
|
||||
@@ -91,6 +91,7 @@ def get_orders_to_be_billed(party_type, party_name):
|
||||
where
|
||||
%s = %s
|
||||
and docstatus = 1
|
||||
and ifnull(status, "") != "Stopped"
|
||||
and ifnull(grand_total, 0) > ifnull(advance_paid, 0)
|
||||
and ifnull(per_billed, 0) < 100.0
|
||||
""" % (voucher_type, 'customer' if party_type == "Customer" else 'supplier', '%s'),
|
||||
|
||||
@@ -31,9 +31,10 @@ class TestPaymentTool(unittest.TestCase):
|
||||
"customer": "_Test Customer 3"
|
||||
})
|
||||
|
||||
jv_against_so1 = self.create_against_jv(jv_test_records[0], {
|
||||
jv_against_so1 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_sales_order": so1.name
|
||||
"against_sales_order": so1.name,
|
||||
"is_advance": "Yes"
|
||||
})
|
||||
|
||||
|
||||
@@ -42,10 +43,11 @@ class TestPaymentTool(unittest.TestCase):
|
||||
"customer": "_Test Customer 3"
|
||||
})
|
||||
|
||||
jv_against_so2 = self.create_against_jv(jv_test_records[0], {
|
||||
jv_against_so2 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_sales_order": so2.name,
|
||||
"credit": 1000
|
||||
"credit": 1000,
|
||||
"is_advance": "Yes"
|
||||
})
|
||||
po = self.create_voucher(po_test_records[1], {
|
||||
"supplier": "_Test Supplier 1"
|
||||
@@ -54,20 +56,20 @@ class TestPaymentTool(unittest.TestCase):
|
||||
#Create SI with partial outstanding
|
||||
si1 = self.create_voucher(si_test_records[0], {
|
||||
"customer": "_Test Customer 3",
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
})
|
||||
|
||||
jv_against_si1 = self.create_against_jv(jv_test_records[0], {
|
||||
|
||||
jv_against_si1 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_invoice": si1.name
|
||||
})
|
||||
#Create SI with no outstanding
|
||||
si2 = self.create_voucher(si_test_records[0], {
|
||||
"customer": "_Test Customer 3",
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
})
|
||||
|
||||
jv_against_si2 = self.create_against_jv(jv_test_records[0], {
|
||||
|
||||
jv_against_si2 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_invoice": si2.name,
|
||||
"credit": 561.80
|
||||
@@ -75,7 +77,7 @@ class TestPaymentTool(unittest.TestCase):
|
||||
|
||||
pi = self.create_voucher(pi_test_records[0], {
|
||||
"supplier": "_Test Supplier 1",
|
||||
"credit_to": "_Test Supplier 1 - _TC"
|
||||
"credit_to": "_Test Supplier 1 - _TC"
|
||||
})
|
||||
|
||||
#Create a dict containing properties and expected values
|
||||
@@ -137,7 +139,7 @@ class TestPaymentTool(unittest.TestCase):
|
||||
payment_tool_doc.set(k, v)
|
||||
|
||||
self.check_outstanding_vouchers(payment_tool_doc, args, expected_outstanding)
|
||||
|
||||
|
||||
|
||||
def check_outstanding_vouchers(self, doc, args, expected_outstanding):
|
||||
from erpnext.accounts.doctype.payment_tool.payment_tool import get_outstanding_vouchers
|
||||
@@ -161,7 +163,7 @@ class TestPaymentTool(unittest.TestCase):
|
||||
|
||||
new_jv = paytool.make_journal_voucher()
|
||||
|
||||
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
||||
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
||||
#against_voucher, against_sales_order, against_purchase_order]
|
||||
expected_values = [
|
||||
[paytool.party_account, 100.00, expected_outstanding.get("Journal Voucher")[0], None, None, None, None],
|
||||
@@ -171,7 +173,7 @@ class TestPaymentTool(unittest.TestCase):
|
||||
[paytool.party_account, 100.00, None, None, None, None, expected_outstanding.get("Purchase Order")[0]]
|
||||
]
|
||||
|
||||
for jv_entry in new_jv.get("entries"):
|
||||
for jv_entry in new_jv.get("entries"):
|
||||
if paytool.party_account == jv_entry.get("account"):
|
||||
row = [
|
||||
jv_entry.get("account"),
|
||||
@@ -183,11 +185,11 @@ class TestPaymentTool(unittest.TestCase):
|
||||
jv_entry.get("against_purchase_order"),
|
||||
]
|
||||
self.assertTrue(row in expected_values)
|
||||
|
||||
|
||||
self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no)
|
||||
self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date)
|
||||
|
||||
def clear_table_entries(self):
|
||||
frappe.db.sql("""delete from `tabGL Entry` where (account = "_Test Customer 3 - _TC" or account = "_Test Supplier 1 - _TC")""")
|
||||
frappe.db.sql("""delete from `tabSales Order` where customer_name = "_Test Customer 3" """)
|
||||
frappe.db.sql("""delete from `tabPurchase Order` where supplier_name = "_Test Supplier 1" """)
|
||||
frappe.db.sql("""delete from `tabPurchase Order` where supplier_name = "_Test Supplier 1" """)
|
||||
|
||||
@@ -142,24 +142,6 @@
|
||||
"reqd": 0,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
@@ -810,6 +792,26 @@
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -821,17 +823,6 @@
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -850,6 +841,17 @@
|
||||
"print_hide": 1,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The unique id for tracking all recurring invoices. It is generated on submit.",
|
||||
@@ -876,7 +878,7 @@
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-09-18 03:12:51.994059",
|
||||
"modified": "2014-10-08 14:23:20.234176",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -169,28 +169,25 @@
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"fieldname": "shipping_address_name",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"in_filter": 1,
|
||||
"label": "Shipping Address Name",
|
||||
"options": "Address",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
"precision": "",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"fieldname": "shipping_address",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"label": "Shipping Address",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "currency_section",
|
||||
@@ -1108,6 +1105,30 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -1120,17 +1141,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.\n",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -1152,6 +1162,17 @@
|
||||
"read_only": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.\n",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The unique id for tracking all recurring invoices.\u00a0It is generated on submit.",
|
||||
@@ -1192,7 +1213,7 @@
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-09-18 03:17:54.976732",
|
||||
"modified": "2014-10-10 16:54:22.284284",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -97,8 +97,7 @@ def validate_account_for_auto_accounting_for_stock(gl_map):
|
||||
|
||||
for entry in gl_map:
|
||||
if entry.account in aii_accounts:
|
||||
frappe.throw(_("Account: {0} can only be updated via \
|
||||
Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
||||
frappe.throw(_("Account: {0} can only be updated via Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
||||
|
||||
|
||||
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"doc_type": "Journal Voucher",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Credit To\"), doc.pay_to_recd_from),\n (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Amount\"), \"<strong>\" + doc.total_amount + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n\n <div class=\"row\">\n <div class=\"col-sm-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-sm-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n\n",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Credit To\"), doc.pay_to_recd_from),\n (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Amount\"), \"<strong>\" + frappe.utils.cstr(doc.total_amount) + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n\n <div class=\"row\">\n <div class=\"col-sm-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-sm-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n\n",
|
||||
"idx": 2,
|
||||
"modified": "2014-08-29 13:20:15.789533",
|
||||
"modified": "2014-10-17 17:20:02.740340",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Credit Note",
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
"doc_type": "Journal Voucher",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Payment Receipt Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Received On\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Received From\"), doc.pay_to_recd_from),\n (_(\"Amount\"), \"<strong>\" + doc.total_amount + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n <div class=\"row\">\n <div class=\"col-sm-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-sm-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Payment Receipt Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Received On\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Received From\"), doc.pay_to_recd_from),\n (_(\"Amount\"), \"<strong>\" + doc.total_amount or 0 + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n <div class=\"row\">\n <div class=\"col-sm-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-sm-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n",
|
||||
"idx": 1,
|
||||
"modified": "2014-08-29 15:55:34.248384",
|
||||
"modified": "2014-11-04 11:25:57.560873",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Receipt Voucher",
|
||||
|
||||
@@ -30,7 +30,8 @@ def execute(filters=None):
|
||||
data = []
|
||||
for gle in entries:
|
||||
if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \
|
||||
or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date:
|
||||
or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date \
|
||||
or (gle.against_voucher_type == "Purchase Order"):
|
||||
voucher_details = voucher_detail_map.get(gle.voucher_type, {}).get(gle.voucher_no, {})
|
||||
|
||||
invoiced_amount = gle.credit > 0 and gle.credit or 0
|
||||
|
||||
@@ -21,7 +21,7 @@ class AccountsReceivableReport(object):
|
||||
def get_columns(self, customer_naming_by):
|
||||
columns = [
|
||||
_("Posting Date") + ":Date:80", _("Account") + ":Link/Account:150",
|
||||
_("Voucher Type") + "::110", _("Voucher No") + "::120", "::30",
|
||||
_("Voucher Type") + "::110", _("Voucher No") + ":Dynamic Link/Voucher Type:120",
|
||||
_("Due Date") + ":Date:80",
|
||||
_("Invoiced Amount") + ":Currency:100", _("Payment Received") + ":Currency:100",
|
||||
_("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", "0-30:Currency:100",
|
||||
@@ -63,11 +63,6 @@ class AccountsReceivableReport(object):
|
||||
|
||||
row += [self.get_territory(gle.account), gle.remarks]
|
||||
data.append(row)
|
||||
|
||||
for i in range(0, len(data)):
|
||||
data[i].insert(4, """<a href="%s"><i class="icon icon-share" style="cursor: pointer;"></i></a>""" \
|
||||
% ("/".join(["#Form", data[i][2], data[i][3]]),))
|
||||
|
||||
return data
|
||||
|
||||
def get_entries_after(self, report_date):
|
||||
@@ -84,6 +79,9 @@ class AccountsReceivableReport(object):
|
||||
return (
|
||||
# advance
|
||||
(not gle.against_voucher) or
|
||||
|
||||
# against sales order
|
||||
(gle.against_voucher_type == "Sales Order") or
|
||||
|
||||
# sales invoice
|
||||
(gle.against_voucher==gle.voucher_no and gle.debit > 0) or
|
||||
|
||||
@@ -35,7 +35,7 @@ def validate_filters(filters, account_details):
|
||||
|
||||
def get_columns():
|
||||
return [_("Posting Date") + ":Date:100", _("Account") + ":Link/Account:200", _("Debit") + ":Float:100",
|
||||
_("Credit") + ":Float:100", _("Voucher Type") + "::120", _("Voucher No") + "::160", _("Link") + "::20",
|
||||
_("Credit") + ":Float:100", _("Voucher Type") + "::120", _("Voucher No") + ":Dynamic Link/Voucher Type:160",
|
||||
_("Against Account") + "::120", _("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"]
|
||||
|
||||
def get_result(filters, account_details):
|
||||
@@ -162,15 +162,6 @@ def get_result_as_list(data):
|
||||
for d in data:
|
||||
result.append([d.get("posting_date"), d.get("account"), d.get("debit"),
|
||||
d.get("credit"), d.get("voucher_type"), d.get("voucher_no"),
|
||||
get_voucher_link(d.get("voucher_type"), d.get("voucher_no")),
|
||||
d.get("against"), d.get("cost_center"), d.get("remarks")])
|
||||
|
||||
return result
|
||||
|
||||
def get_voucher_link(voucher_type, voucher_no):
|
||||
icon = ""
|
||||
if voucher_type and voucher_no:
|
||||
icon = """<a href="%s"><i class="icon icon-share" style="cursor: pointer;">
|
||||
</i></a>""" % ("/".join(["#Form", voucher_type, voucher_no]))
|
||||
|
||||
return icon
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import cstr, flt
|
||||
from frappe.utils import flt
|
||||
from frappe import _
|
||||
|
||||
from erpnext.stock.doctype.item.item import get_last_purchase_details
|
||||
from erpnext.controllers.buying_controller import BuyingController
|
||||
|
||||
class PurchaseCommon(BuyingController):
|
||||
|
||||
def update_last_purchase_rate(self, obj, is_submit):
|
||||
"""updates last_purchase_rate in item table for each item"""
|
||||
|
||||
@@ -123,27 +121,6 @@ class PurchaseCommon(BuyingController):
|
||||
else:
|
||||
chk_dupl_itm.append(f)
|
||||
|
||||
def get_qty(self, curr_doctype, ref_tab_fname, ref_tab_dn, ref_doc_tname, transaction, curr_parent_name):
|
||||
# Get total Quantities of current doctype (eg. PR) except for qty of this transaction
|
||||
#------------------------------
|
||||
# please check as UOM changes from Material Request - Purchase Order ,so doing following else uom should be same .
|
||||
# i.e. in PO uom is NOS then in PR uom should be NOS
|
||||
# but if in Material Request uom KG it can change in PO
|
||||
|
||||
get_qty = (transaction == 'Material Request - Purchase Order') and 'qty * conversion_factor' or 'qty'
|
||||
qty = frappe.db.sql("""select sum(%s) from `tab%s` where %s = %s and
|
||||
docstatus = 1 and parent != %s""" % (get_qty, curr_doctype, ref_tab_fname, '%s', '%s'),
|
||||
(ref_tab_dn, curr_parent_name))
|
||||
qty = qty and flt(qty[0][0]) or 0
|
||||
|
||||
# get total qty of ref doctype
|
||||
#--------------------
|
||||
max_qty = frappe.db.sql("""select qty from `tab%s` where name = %s
|
||||
and docstatus = 1""" % (ref_doc_tname, '%s'), ref_tab_dn)
|
||||
max_qty = max_qty and flt(max_qty[0][0]) or 0
|
||||
|
||||
return cstr(qty)+'~~~'+cstr(max_qty)
|
||||
|
||||
def check_for_stopped_status(self, doctype, docname):
|
||||
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
|
||||
status = 'Stopped'""" % (doctype, '%s'), docname)
|
||||
|
||||
@@ -101,24 +101,6 @@
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"description": "Start date of current order's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"description": "End date of current order's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
@@ -705,6 +687,26 @@
|
||||
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Start date of current order's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "End date of current order's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -716,17 +718,6 @@
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -745,6 +736,17 @@
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"fieldname": "recurring_id",
|
||||
@@ -770,7 +772,7 @@
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-09-18 03:16:06.299317",
|
||||
"modified": "2014-10-08 14:23:29.718779",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -96,50 +96,45 @@ class PurchaseOrder(BuyingController):
|
||||
check_list.append(d.prevdoc_docname)
|
||||
pc_obj.check_for_stopped_status( d.prevdoc_doctype, d.prevdoc_docname)
|
||||
|
||||
def update_requested_qty(self):
|
||||
material_request_map = {}
|
||||
for d in self.get("po_details"):
|
||||
if d.prevdoc_doctype and d.prevdoc_doctype == "Material Request" and d.prevdoc_detail_docname:
|
||||
material_request_map.setdefault(d.prevdoc_docname, []).append(d.prevdoc_detail_docname)
|
||||
|
||||
def update_bin(self, is_submit, is_stopped = 0):
|
||||
from erpnext.stock.utils import update_bin
|
||||
pc_obj = frappe.get_doc('Purchase Common')
|
||||
for d in self.get('po_details'):
|
||||
#1. Check if is_stock_item == 'Yes'
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes":
|
||||
# this happens when item is changed from non-stock to stock item
|
||||
if not d.warehouse:
|
||||
continue
|
||||
for mr, mr_item_rows in material_request_map.items():
|
||||
if mr and mr_item_rows:
|
||||
mr_obj = frappe.get_doc("Material Request", mr)
|
||||
|
||||
ind_qty, po_qty = 0, flt(d.qty) * flt(d.conversion_factor)
|
||||
if is_stopped:
|
||||
po_qty = flt(d.qty) > flt(d.received_qty) and \
|
||||
flt( flt(flt(d.qty) - flt(d.received_qty))*flt(d.conversion_factor)) or 0
|
||||
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||
frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError)
|
||||
|
||||
# No updates in Material Request on Stop / Unstop
|
||||
if cstr(d.prevdoc_doctype) == 'Material Request' and not is_stopped:
|
||||
# get qty and pending_qty of prevdoc
|
||||
curr_ref_qty = pc_obj.get_qty(d.doctype, 'prevdoc_detail_docname',
|
||||
d.prevdoc_detail_docname, 'Material Request Item',
|
||||
'Material Request - Purchase Order', self.name)
|
||||
max_qty, qty, curr_qty = flt(curr_ref_qty.split('~~~')[1]), \
|
||||
flt(curr_ref_qty.split('~~~')[0]), 0
|
||||
mr_obj.update_requested_qty(mr_item_rows)
|
||||
|
||||
if flt(qty) + flt(po_qty) > flt(max_qty):
|
||||
curr_qty = flt(max_qty) - flt(qty)
|
||||
# special case as there is no restriction
|
||||
# for Material Request - Purchase Order
|
||||
curr_qty = curr_qty > 0 and curr_qty or 0
|
||||
else:
|
||||
curr_qty = flt(po_qty)
|
||||
def update_ordered_qty(self, po_item_rows=None):
|
||||
"""update requested qty (before ordered_qty is updated)"""
|
||||
from erpnext.stock.utils import get_bin
|
||||
|
||||
ind_qty = -flt(curr_qty)
|
||||
def _update_ordered_qty(item_code, warehouse):
|
||||
ordered_qty = frappe.db.sql("""
|
||||
select sum((po_item.qty - ifnull(po_item.received_qty, 0))*po_item.conversion_factor)
|
||||
from `tabPurchase Order Item` po_item, `tabPurchase Order` po
|
||||
where po_item.item_code=%s and po_item.warehouse=%s
|
||||
and po_item.qty > ifnull(po_item.received_qty, 0) and po_item.parent=po.name
|
||||
and po.status!='Stopped' and po.docstatus=1""", (item_code, warehouse))
|
||||
|
||||
# Update ordered_qty and indented_qty in bin
|
||||
args = {
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.warehouse,
|
||||
"ordered_qty": (is_submit and 1 or -1) * flt(po_qty),
|
||||
"indented_qty": (is_submit and 1 or -1) * flt(ind_qty),
|
||||
"posting_date": self.transaction_date
|
||||
}
|
||||
update_bin(args)
|
||||
bin_doc = get_bin(item_code, warehouse)
|
||||
bin_doc.ordered_qty = flt(ordered_qty[0][0]) if ordered_qty else 0
|
||||
bin_doc.save()
|
||||
|
||||
item_wh_list = []
|
||||
for d in self.get("po_details"):
|
||||
if (not po_item_rows or d.name in po_item_rows) and [d.item_code, d.warehouse] not in item_wh_list \
|
||||
and frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" and d.warehouse:
|
||||
item_wh_list.append([d.item_code, d.warehouse])
|
||||
|
||||
for item_code, warehouse in item_wh_list:
|
||||
_update_ordered_qty(item_code, warehouse)
|
||||
|
||||
def check_modified_date(self):
|
||||
mod_db = frappe.db.sql("select modified from `tabPurchase Order` where name = %s",
|
||||
@@ -152,13 +147,11 @@ class PurchaseOrder(BuyingController):
|
||||
|
||||
def update_status(self, status):
|
||||
self.check_modified_date()
|
||||
# step 1:=> Set Status
|
||||
frappe.db.set(self,'status',cstr(status))
|
||||
|
||||
# step 2:=> Update Bin
|
||||
self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1)
|
||||
self.update_requested_qty()
|
||||
self.update_ordered_qty()
|
||||
|
||||
# step 3:=> Acknowledge user
|
||||
msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status))
|
||||
|
||||
def on_submit(self):
|
||||
@@ -167,7 +160,8 @@ class PurchaseOrder(BuyingController):
|
||||
purchase_controller = frappe.get_doc("Purchase Common")
|
||||
|
||||
self.update_prevdoc_status()
|
||||
self.update_bin(is_submit = 1, is_stopped = 0)
|
||||
self.update_requested_qty()
|
||||
self.update_ordered_qty()
|
||||
|
||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||
self.company, self.grand_total)
|
||||
@@ -192,8 +186,13 @@ class PurchaseOrder(BuyingController):
|
||||
throw(_("Purchase Invoice {0} is already submitted").format(", ".join(submitted)))
|
||||
|
||||
frappe.db.set(self,'status','Cancelled')
|
||||
|
||||
self.update_prevdoc_status()
|
||||
self.update_bin( is_submit = 0, is_stopped = 0)
|
||||
|
||||
# Must be called after updating ordered qty in Material Request
|
||||
self.update_requested_qty()
|
||||
self.update_ordered_qty()
|
||||
|
||||
pc_obj.update_last_purchase_rate(self, is_submit = 0)
|
||||
|
||||
def on_update(self):
|
||||
|
||||
@@ -29,8 +29,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
frappe.get_doc(pr).insert()
|
||||
|
||||
def test_ordered_qty(self):
|
||||
frappe.db.sql("delete from tabBin")
|
||||
|
||||
existing_ordered_qty = self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC")
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
|
||||
|
||||
po = frappe.copy_doc(test_records[0]).insert()
|
||||
@@ -43,8 +42,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
po.get("po_details")[0].item_code = "_Test Item"
|
||||
po.submit()
|
||||
|
||||
self.assertEquals(frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty"), 10)
|
||||
self.assertEquals(self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC"), existing_ordered_qty + 10)
|
||||
|
||||
pr = make_purchase_receipt(po.name)
|
||||
|
||||
@@ -56,8 +54,9 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
pr.insert()
|
||||
pr.submit()
|
||||
|
||||
self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 6.0)
|
||||
po.load_from_db()
|
||||
self.assertEquals(po.get("po_details")[0].received_qty, 4)
|
||||
self.assertEquals(self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC"), existing_ordered_qty + 6)
|
||||
|
||||
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
|
||||
|
||||
@@ -68,8 +67,16 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
pr1.insert()
|
||||
pr1.submit()
|
||||
|
||||
self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 0.0)
|
||||
po.load_from_db()
|
||||
self.assertEquals(po.get("po_details")[0].received_qty, 12)
|
||||
self.assertEquals(self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC"), existing_ordered_qty)
|
||||
|
||||
pr1.load_from_db()
|
||||
pr1.cancel()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEquals(po.get("po_details")[0].received_qty, 4)
|
||||
self.assertEquals(self._get_ordered_qty("_Test Item", "_Test Warehouse - _TC"), existing_ordered_qty + 6)
|
||||
|
||||
def test_make_purchase_invoice(self):
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice
|
||||
@@ -111,7 +118,10 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||
test_recurring_document(self, test_records)
|
||||
|
||||
def _get_ordered_qty(self, item_code, warehouse):
|
||||
return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "ordered_qty"))
|
||||
|
||||
test_dependencies = ["BOM"]
|
||||
|
||||
test_dependencies = ["BOM", "Item Price"]
|
||||
|
||||
test_records = frappe.get_test_records('Purchase Order')
|
||||
|
||||
@@ -142,10 +142,10 @@ def get_data():
|
||||
"doctype": "Item",
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "stock-balance",
|
||||
"label": _("Stock Balance"),
|
||||
"icon": "icon-table",
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Stock Balance",
|
||||
"doctype": "Warehouse"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
@@ -170,13 +170,7 @@ def get_data():
|
||||
"name": "stock-analytics",
|
||||
"label": _("Stock Analytics"),
|
||||
"icon": "icon-bar-chart"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Warehouse-Wise Stock Balance",
|
||||
"doctype": "Warehouse"
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -387,7 +387,7 @@ class AccountsController(TransactionBase):
|
||||
|
||||
res = frappe.db.sql("""
|
||||
select
|
||||
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no
|
||||
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no, `against_%s` as against_order
|
||||
from
|
||||
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
||||
where
|
||||
@@ -400,7 +400,7 @@ class AccountsController(TransactionBase):
|
||||
and ifnull(t2.against_purchase_order, '') = ''
|
||||
) %s)
|
||||
order by t1.posting_date""" %
|
||||
(dr_or_cr, '%s', cond),
|
||||
(dr_or_cr, against_order_field, '%s', cond),
|
||||
tuple([account_head] + so_list), as_dict= True)
|
||||
|
||||
self.set(parentfield, [])
|
||||
@@ -411,7 +411,7 @@ class AccountsController(TransactionBase):
|
||||
"jv_detail_no": d.jv_detail_no,
|
||||
"remarks": d.remark,
|
||||
"advance_amount": flt(d.amount),
|
||||
"allocate_amount": 0
|
||||
"allocated_amount": flt(d.amount) if d.against_order else 0
|
||||
})
|
||||
|
||||
def validate_advance_jv(self, advance_table_fieldname, against_order_field):
|
||||
|
||||
@@ -256,8 +256,6 @@ class BuyingController(StockController):
|
||||
rm.required_qty = required_qty
|
||||
|
||||
rm.conversion_factor = item.conversion_factor
|
||||
rm.rate = bom_item.rate
|
||||
rm.amount = required_qty * flt(bom_item.rate)
|
||||
rm.idx = rm_supplied_idx
|
||||
|
||||
if self.doctype == "Purchase Receipt":
|
||||
@@ -268,7 +266,25 @@ class BuyingController(StockController):
|
||||
|
||||
rm_supplied_idx += 1
|
||||
|
||||
raw_materials_cost += required_qty * flt(bom_item.rate)
|
||||
# get raw materials rate
|
||||
if self.doctype == "Purchase Receipt":
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
rm.rate = get_incoming_rate({
|
||||
"item_code": bom_item.item_code,
|
||||
"warehouse": self.supplier_warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": -1 * required_qty,
|
||||
"serial_no": rm.serial_no
|
||||
})
|
||||
if not rm.rate:
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse)
|
||||
else:
|
||||
rm.rate = bom_item.rate
|
||||
|
||||
rm.amount = required_qty * flt(rm.rate)
|
||||
raw_materials_cost += flt(rm.amount)
|
||||
|
||||
if self.doctype == "Purchase Receipt":
|
||||
item.rm_supp_cost = raw_materials_cost
|
||||
|
||||
@@ -8,7 +8,7 @@ from frappe import msgprint, _
|
||||
import frappe.defaults
|
||||
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries
|
||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
|
||||
|
||||
class StockController(AccountsController):
|
||||
def make_gl_entries(self, repost_future_gle=True):
|
||||
@@ -24,11 +24,12 @@ class StockController(AccountsController):
|
||||
|
||||
if repost_future_gle:
|
||||
items, warehouses = self.get_items_and_warehouses()
|
||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items, warehouse_account)
|
||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
|
||||
warehouse_account)
|
||||
|
||||
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
|
||||
default_cost_center=None):
|
||||
from erpnext.accounts.general_ledger import process_gl_map
|
||||
|
||||
if not warehouse_account:
|
||||
warehouse_account = get_warehouse_account()
|
||||
|
||||
@@ -118,7 +119,8 @@ class StockController(AccountsController):
|
||||
|
||||
def get_stock_ledger_details(self):
|
||||
stock_ledger = {}
|
||||
for sle in frappe.db.sql("""select warehouse, stock_value_difference, voucher_detail_no
|
||||
for sle in frappe.db.sql("""select warehouse, stock_value_difference,
|
||||
voucher_detail_no, item_code, posting_date, actual_qty
|
||||
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
|
||||
(self.doctype, self.name), as_dict=True):
|
||||
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
||||
@@ -173,7 +175,7 @@ class StockController(AccountsController):
|
||||
_(self.doctype), self.name, item.get("item_code")))
|
||||
|
||||
def get_sl_entries(self, d, args):
|
||||
sl_dict = {
|
||||
sl_dict = frappe._dict({
|
||||
"item_code": d.get("item_code", None),
|
||||
"warehouse": d.get("warehouse", None),
|
||||
"posting_date": self.posting_date,
|
||||
@@ -190,7 +192,7 @@ class StockController(AccountsController):
|
||||
"serial_no": d.get("serial_no"),
|
||||
"project": d.get("project_name"),
|
||||
"is_cancelled": self.docstatus==2 and "Yes" or "No"
|
||||
}
|
||||
})
|
||||
|
||||
sl_dict.update(args)
|
||||
return sl_dict
|
||||
@@ -214,7 +216,8 @@ class StockController(AccountsController):
|
||||
|
||||
return serialized_items
|
||||
|
||||
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None):
|
||||
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
|
||||
warehouse_account=None):
|
||||
def _delete_gl_entries(voucher_type, voucher_no):
|
||||
frappe.db.sql("""delete from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
@@ -4,7 +4,7 @@ app_publisher = "Web Notes Technologies Pvt. Ltd. and Contributors"
|
||||
app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
|
||||
app_icon = "icon-th"
|
||||
app_color = "#e74c3c"
|
||||
app_version = "4.5.0"
|
||||
app_version = "4.11.0"
|
||||
|
||||
error_report_email = "support@erpnext.com"
|
||||
|
||||
@@ -47,8 +47,8 @@ doc_events = {
|
||||
"on_update": "erpnext.home.make_comment_feed"
|
||||
},
|
||||
"Stock Entry": {
|
||||
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_qty",
|
||||
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_qty"
|
||||
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
|
||||
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty"
|
||||
},
|
||||
"User": {
|
||||
"validate": "erpnext.hr.doctype.employee.employee.validate_employee_role",
|
||||
|
||||
@@ -115,6 +115,9 @@ class BOM(Document):
|
||||
return rate
|
||||
|
||||
def update_cost(self):
|
||||
if self.docstatus == 2:
|
||||
return
|
||||
|
||||
for d in self.get("bom_materials"):
|
||||
d.rate = self.get_bom_material_detail({
|
||||
'item_code': d.item_code,
|
||||
@@ -122,9 +125,10 @@ class BOM(Document):
|
||||
'qty': d.qty
|
||||
})["rate"]
|
||||
|
||||
if self.docstatus in (0, 1):
|
||||
if self.docstatus == 1:
|
||||
self.ignore_validate_update_after_submit = True
|
||||
self.save()
|
||||
self.calculate_cost()
|
||||
self.save()
|
||||
|
||||
def get_bom_unitcost(self, bom_no):
|
||||
bom = frappe.db.sql("""select name, total_variable_cost/quantity as unit_cost from `tabBOM`
|
||||
@@ -269,29 +273,27 @@ class BOM(Document):
|
||||
"""Calculate bom totals"""
|
||||
self.calculate_op_cost()
|
||||
self.calculate_rm_cost()
|
||||
self.calculate_fixed_cost()
|
||||
self.total_variable_cost = self.raw_material_cost + self.operating_cost
|
||||
self.total_cost = self.total_variable_cost + self.total_fixed_cost
|
||||
|
||||
def calculate_op_cost(self):
|
||||
"""Update workstation rate and calculates totals"""
|
||||
total_op_cost = 0
|
||||
total_op_cost, fixed_cost = 0, 0
|
||||
for d in self.get('bom_operations'):
|
||||
if d.workstation and not d.hour_rate:
|
||||
d.hour_rate = frappe.db.get_value("Workstation", d.workstation, "hour_rate")
|
||||
if d.workstation:
|
||||
w = frappe.db.get_value("Workstation", d.workstation, ["hour_rate", "fixed_cycle_cost"])
|
||||
if not d.hour_rate:
|
||||
d.hour_rate = flt(w[0])
|
||||
|
||||
fixed_cost += flt(w[1])
|
||||
|
||||
if d.hour_rate and d.time_in_mins:
|
||||
d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
|
||||
total_op_cost += flt(d.operating_cost)
|
||||
|
||||
self.operating_cost = total_op_cost
|
||||
|
||||
def calculate_fixed_cost(self):
|
||||
"""Update workstation rate and calculates totals"""
|
||||
fixed_cost = 0
|
||||
for d in self.get('bom_operations'):
|
||||
if d.workstation:
|
||||
fixed_cost += flt(frappe.db.get_value("Workstation", d.workstation, "fixed_cycle_cost"))
|
||||
self.total_fixed_cost = fixed_cost
|
||||
|
||||
|
||||
def calculate_rm_cost(self):
|
||||
"""Fetch RM rate as per today's valuation rate and calculate totals"""
|
||||
total_rm_cost = 0
|
||||
|
||||
@@ -96,7 +96,7 @@ cur_frm.cscript['Transfer Raw Materials'] = function() {
|
||||
}
|
||||
|
||||
cur_frm.cscript['Update Finished Goods'] = function() {
|
||||
cur_frm.cscript.make_se('Manufacture/Repack');
|
||||
cur_frm.cscript.make_se('Manufacture');
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['production_item'].get_query = function(doc) {
|
||||
|
||||
@@ -109,15 +109,15 @@
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.docstatus==1",
|
||||
"description": "Automatically updated via Stock Entry of type Manufacture/Repack",
|
||||
"fieldname": "produced_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Manufactured Qty",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "produced_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"depends_on": "eval:doc.docstatus==1",
|
||||
"description": "Automatically updated via Stock Entry of type Manufacture or Repack",
|
||||
"fieldname": "produced_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Manufactured Qty",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "produced_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
|
||||
@@ -103,7 +103,7 @@ class ProductionOrder(Document):
|
||||
status = "Submitted"
|
||||
if stock_entries:
|
||||
status = "In Process"
|
||||
produced_qty = stock_entries.get("Manufacture/Repack")
|
||||
produced_qty = stock_entries.get("Manufacture")
|
||||
if flt(produced_qty) == flt(self.qty):
|
||||
status = "Completed"
|
||||
|
||||
@@ -113,7 +113,7 @@ class ProductionOrder(Document):
|
||||
def update_produced_qty(self):
|
||||
produced_qty = frappe.db.sql("""select sum(fg_completed_qty)
|
||||
from `tabStock Entry` where production_order=%s and docstatus=1
|
||||
and purpose='Manufacture/Repack'""", self.name)
|
||||
and purpose='Manufacture'""", self.name)
|
||||
produced_qty = flt(produced_qty[0][0]) if produced_qty else 0
|
||||
|
||||
if produced_qty > self.qty:
|
||||
|
||||
@@ -31,7 +31,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
s.submit()
|
||||
|
||||
# from wip to fg
|
||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture/Repack", 4))
|
||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 4))
|
||||
s.insert()
|
||||
s.submit()
|
||||
|
||||
@@ -49,7 +49,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
test_stock_entry.make_stock_entry("_Test Item", None, "_Test Warehouse - _TC", 100, 100)
|
||||
test_stock_entry.make_stock_entry("_Test Item Home Desktop 100", None, "_Test Warehouse - _TC", 100, 100)
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture/Repack", 7))
|
||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 7))
|
||||
s.insert()
|
||||
|
||||
self.assertRaises(StockOverProductionError, s.submit)
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2014-06-03 07:18:17.082436",
|
||||
"modified": "2014-09-17 12:41:55.740299",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Issued Items Against Production Order",
|
||||
"owner": "Administrator",
|
||||
"query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture/Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code",
|
||||
"query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture' or 'Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code",
|
||||
"ref_doctype": "Production Order",
|
||||
"report_name": "Issued Items Against Production Order",
|
||||
"report_type": "Query Report"
|
||||
|
||||
@@ -80,4 +80,11 @@ execute:frappe.delete_doc("DocType", "Landed Cost Wizard")
|
||||
erpnext.patches.v4_2.default_website_style
|
||||
erpnext.patches.v4_2.set_company_country
|
||||
erpnext.patches.v4_2.update_sales_order_invoice_field_name
|
||||
erpnext.patches.v4_2.cost_of_production_cycle
|
||||
erpnext.patches.v4_2.cost_of_production_cycle
|
||||
erpnext.patches.v4_2.seprate_manufacture_and_repack
|
||||
execute:frappe.delete_doc("Report", "Warehouse-Wise Stock Balance")
|
||||
execute:frappe.delete_doc("DocType", "Purchase Request")
|
||||
execute:frappe.delete_doc("DocType", "Purchase Request Item")
|
||||
erpnext.patches.v4_2.recalculate_bom_cost
|
||||
erpnext.patches.v4_2.fix_gl_entries_for_stock_transactions
|
||||
erpnext.patches.v4_2.update_requested_and_ordered_qty
|
||||
|
||||
@@ -2,6 +2,7 @@ import frappe
|
||||
from frappe.templates.pages.style_settings import default_properties
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('website', 'doctype', 'style_settings')
|
||||
style_settings = frappe.get_doc("Style Settings", "Style Settings")
|
||||
if not style_settings.apply_style:
|
||||
style_settings.update(default_properties)
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
|
||||
def execute():
|
||||
from erpnext.utilities.repost_stock import repost
|
||||
repost(allow_zero_rate=True, only_actual=True)
|
||||
|
||||
warehouse_account = frappe.db.sql("""select name, master_name from tabAccount
|
||||
where ifnull(account_type, '') = 'Warehouse'""")
|
||||
if warehouse_account:
|
||||
warehouses = [d[1] for d in warehouse_account]
|
||||
accounts = [d[0] for d in warehouse_account]
|
||||
|
||||
stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
||||
from `tabStock Ledger Entry` sle
|
||||
where sle.warehouse in (%s)
|
||||
order by sle.posting_date""" %
|
||||
', '.join(['%s']*len(warehouses)), tuple(warehouses))
|
||||
|
||||
rejected = []
|
||||
for voucher_type, voucher_no in stock_vouchers:
|
||||
stock_bal = frappe.db.sql("""select sum(stock_value_difference) from `tabStock Ledger Entry`
|
||||
where voucher_type=%s and voucher_no =%s and warehouse in (%s)""" %
|
||||
('%s', '%s', ', '.join(['%s']*len(warehouses))), tuple([voucher_type, voucher_no] + warehouses))
|
||||
|
||||
account_bal = frappe.db.sql("""select ifnull(sum(ifnull(debit, 0) - ifnull(credit, 0)), 0)
|
||||
from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no =%s and account in (%s)
|
||||
group by voucher_type, voucher_no""" %
|
||||
('%s', '%s', ', '.join(['%s']*len(accounts))), tuple([voucher_type, voucher_no] + accounts))
|
||||
|
||||
if stock_bal and account_bal and abs(flt(stock_bal[0][0]) - flt(account_bal[0][0])) > 0.1:
|
||||
try:
|
||||
print voucher_type, voucher_no, stock_bal[0][0], account_bal[0][0]
|
||||
|
||||
frappe.db.sql("""delete from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
voucher = frappe.get_doc(voucher_type, voucher_no)
|
||||
voucher.make_gl_entries(repost_future_gle=False)
|
||||
frappe.db.commit()
|
||||
except Exception, e:
|
||||
print frappe.get_traceback()
|
||||
rejected.append([voucher_type, voucher_no])
|
||||
frappe.db.rollback()
|
||||
|
||||
print "Failed to repost: "
|
||||
print rejected
|
||||
16
erpnext/patches/v4_2/recalculate_bom_cost.py
Normal file
16
erpnext/patches/v4_2/recalculate_bom_cost.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for d in frappe.db.sql("select name from `tabBOM` where docstatus < 2"):
|
||||
try:
|
||||
document = frappe.get_doc('BOM', d[0])
|
||||
if document.docstatus == 1:
|
||||
document.ignore_validate_update_after_submit = True
|
||||
document.calculate_cost()
|
||||
document.save()
|
||||
except:
|
||||
pass
|
||||
9
erpnext/patches/v4_2/seprate_manufacture_and_repack.py
Normal file
9
erpnext/patches/v4_2/seprate_manufacture_and_repack.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.db.sql("""update `tabStock Entry` set purpose='Manufacture' where purpose='Manufacture/Repack' and ifnull(production_order,"")!="" """)
|
||||
frappe.db.sql("""update `tabStock Entry` set purpose='Repack' where purpose='Manufacture/Repack' and ifnull(production_order,"")="" """)
|
||||
24
erpnext/patches/v4_2/update_requested_and_ordered_qty.py
Normal file
24
erpnext/patches/v4_2/update_requested_and_ordered_qty.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
from erpnext.utilities.repost_stock import update_bin_qty, get_indented_qty, get_ordered_qty
|
||||
|
||||
count=0
|
||||
for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse from
|
||||
(select item_code, warehouse from tabBin
|
||||
union
|
||||
select item_code, warehouse from `tabStock Ledger Entry`) a"""):
|
||||
try:
|
||||
count += 1
|
||||
update_bin_qty(item_code, warehouse, {
|
||||
"indented_qty": get_indented_qty(item_code, warehouse),
|
||||
"ordered_qty": get_ordered_qty(item_code, warehouse)
|
||||
})
|
||||
if count % 200 == 0:
|
||||
frappe.db.commit()
|
||||
except:
|
||||
frappe.db.rollback()
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"allow_import": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-04-03 16:38:41",
|
||||
"description": "Log of Activities performed by users against Tasks that can be used for tracking time, billing.",
|
||||
@@ -25,14 +25,6 @@
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "hours",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Hours",
|
||||
"permlevel": 0,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "to_time",
|
||||
"fieldtype": "Datetime",
|
||||
@@ -42,6 +34,14 @@
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "hours",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Hours",
|
||||
"permlevel": 0,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
@@ -151,7 +151,7 @@
|
||||
"icon": "icon-time",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-08-04 05:23:15.740050",
|
||||
"modified": "2014-10-22 16:53:26.993828",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Time Log",
|
||||
|
||||
@@ -131,6 +131,7 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
||||
|
||||
if(me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) {
|
||||
var item = me.item_by_name[sl.item_code];
|
||||
if(item.closing_qty_value==undefined) item.closing_qty_value = 0;
|
||||
|
||||
if(me.value_or_qty!="Quantity") {
|
||||
var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
|
||||
@@ -138,9 +139,20 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
||||
item.valuation_method : sys_defaults.valuation_method;
|
||||
var is_fifo = valuation_method == "FIFO";
|
||||
|
||||
var diff = me.get_value_diff(wh, sl, is_fifo);
|
||||
if(sl.voucher_type=="Stock Reconciliation") {
|
||||
var diff = (sl.qty_after_transaction * sl.valuation_rate) - item.closing_qty_value;
|
||||
wh.fifo_stack.push([sl.qty_after_transaction, sl.valuation_rate, sl.posting_date]);
|
||||
wh.balance_qty = sl.qty_after_transaction;
|
||||
wh.balance_value = sl.valuation_rate * sl.qty_after_transaction;
|
||||
} else {
|
||||
var diff = me.get_value_diff(wh, sl, is_fifo);
|
||||
}
|
||||
} else {
|
||||
var diff = sl.qty;
|
||||
if(sl.voucher_type=="Stock Reconciliation") {
|
||||
var diff = sl.qty_after_transaction - item.closing_qty_value;
|
||||
} else {
|
||||
var diff = sl.qty;
|
||||
}
|
||||
}
|
||||
|
||||
if(posting_datetime < from_date) {
|
||||
@@ -150,6 +162,8 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
item.closing_qty_value += diff;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,8 +9,8 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
};
|
||||
return this.item_warehouse[item][warehouse];
|
||||
},
|
||||
|
||||
get_value_diff: function(wh, sl, is_fifo) {
|
||||
|
||||
get_value_diff: function(wh, sl, is_fifo) {
|
||||
// value
|
||||
if(sl.qty > 0) {
|
||||
// incoming - rate is given
|
||||
@@ -30,9 +30,9 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
} else {
|
||||
var value_diff = (rate * add_qty);
|
||||
}
|
||||
|
||||
|
||||
if(add_qty)
|
||||
wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]);
|
||||
wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]);
|
||||
} else {
|
||||
// called everytime for maintaining fifo stack
|
||||
var fifo_value_diff = this.get_fifo_value_diff(wh, sl);
|
||||
@@ -44,13 +44,13 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
var value_diff = fifo_value_diff;
|
||||
} else {
|
||||
// average rate for weighted average
|
||||
var rate = (wh.balance_qty.toFixed(2) == 0.00 ? 0 :
|
||||
var rate = (wh.balance_qty.toFixed(2) == 0.00 ? 0 :
|
||||
flt(wh.balance_value) / flt(wh.balance_qty));
|
||||
|
||||
|
||||
// no change in value if negative qty
|
||||
if((wh.balance_qty + sl.qty).toFixed(2) >= 0.00)
|
||||
var value_diff = (rate * sl.qty);
|
||||
else
|
||||
else
|
||||
var value_diff = -wh.balance_value;
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,6 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
// update balance (only needed in case of valuation)
|
||||
wh.balance_qty += sl.qty;
|
||||
wh.balance_value += value_diff;
|
||||
|
||||
return value_diff;
|
||||
},
|
||||
get_fifo_value_diff: function(wh, sl) {
|
||||
@@ -66,19 +65,19 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
var fifo_stack = (wh.fifo_stack || []).reverse();
|
||||
var fifo_value_diff = 0.0;
|
||||
var qty = -sl.qty;
|
||||
|
||||
|
||||
for(var i=0, j=fifo_stack.length; i<j; i++) {
|
||||
var batch = fifo_stack.pop();
|
||||
if(batch[0] >= qty) {
|
||||
batch[0] = batch[0] - qty;
|
||||
fifo_value_diff += (qty * batch[1]);
|
||||
|
||||
|
||||
qty = 0.0;
|
||||
if(batch[0]) {
|
||||
// batch still has qty put it back
|
||||
fifo_stack.push(batch);
|
||||
}
|
||||
|
||||
|
||||
// all qty found
|
||||
break;
|
||||
} else {
|
||||
@@ -87,35 +86,34 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
qty = qty - batch[0];
|
||||
}
|
||||
}
|
||||
|
||||
// reset the updated stack
|
||||
wh.fifo_stack = fifo_stack.reverse();
|
||||
return -fifo_value_diff;
|
||||
},
|
||||
|
||||
|
||||
get_serialized_value_diff: function(sl) {
|
||||
var me = this;
|
||||
|
||||
|
||||
var value_diff = 0.0;
|
||||
|
||||
|
||||
$.each(sl.serial_no.trim().split("\n"), function(i, sr) {
|
||||
if(sr) {
|
||||
value_diff += flt(me.serialized_buying_rates[sr.trim().toLowerCase()]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return value_diff;
|
||||
},
|
||||
|
||||
|
||||
get_serialized_buying_rates: function() {
|
||||
var serialized_buying_rates = {};
|
||||
|
||||
|
||||
if (frappe.report_dump.data["Serial No"]) {
|
||||
$.each(frappe.report_dump.data["Serial No"], function(i, sn) {
|
||||
serialized_buying_rates[sn.name.toLowerCase()] = flt(sn.incoming_rate);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return serialized_buying_rates;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ class Quotation(SellingController):
|
||||
self.validate_order_type()
|
||||
self.validate_for_items()
|
||||
self.validate_uom_is_integer("stock_uom", "qty")
|
||||
self.quotation_to = "Customer" if self.customer else "Lead"
|
||||
self.validate_quotation_to()
|
||||
|
||||
def has_sales_order(self):
|
||||
return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1})
|
||||
@@ -54,6 +54,13 @@ class Quotation(SellingController):
|
||||
if is_sales_item == 'No':
|
||||
frappe.throw(_("Item {0} must be Sales Item").format(d.item_code))
|
||||
|
||||
def validate_quotation_to(self):
|
||||
if self.customer:
|
||||
self.quotation_to = "Customer"
|
||||
self.lead = None
|
||||
elif self.lead:
|
||||
self.quotation_to = "Lead"
|
||||
|
||||
def update_opportunity(self):
|
||||
for opportunity in list(set([d.prevdoc_docname for d in self.get("quotation_details")])):
|
||||
if opportunity:
|
||||
@@ -139,8 +146,8 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
|
||||
return doclist
|
||||
|
||||
def _make_customer(source_name, ignore_permissions=False):
|
||||
quotation = frappe.db.get_value("Quotation", source_name, ["lead", "order_type"])
|
||||
if quotation and quotation[0]:
|
||||
quotation = frappe.db.get_value("Quotation", source_name, ["lead", "order_type", "customer"])
|
||||
if quotation and quotation[0] and not quotation[2]:
|
||||
lead_name = quotation[0]
|
||||
customer_name = frappe.db.get_value("Customer", {"lead_name": lead_name},
|
||||
["name", "customer_name"], as_dict=True)
|
||||
|
||||
@@ -169,24 +169,6 @@
|
||||
"search_index": 1,
|
||||
"width": "160px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"description": "Start date of current order's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"description": "End date of current order's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "Customer's Purchase Order Number",
|
||||
"fieldname": "po_no",
|
||||
@@ -951,6 +933,26 @@
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Start date of current order's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "End date of current order's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -962,17 +964,6 @@
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -991,6 +982,17 @@
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"fieldname": "recurring_id",
|
||||
@@ -1018,7 +1020,7 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"modified": "2014-09-18 03:17:33.241162",
|
||||
"modified": "2014-10-08 14:22:44.717108",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
|
||||
@@ -587,16 +587,18 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
||||
});
|
||||
|
||||
frappe.ui.form.on(cur_frm.doctype,"project_name", function(frm) {
|
||||
frappe.call({
|
||||
method:'erpnext.projects.doctype.project.project.get_cost_center_name' ,
|
||||
args: { project_name: frm.doc.project_name },
|
||||
callback: function(r, rt) {
|
||||
if(!r.exc) {
|
||||
$.each(frm.doc[cur_frm.cscript.fname] || [], function(i, row) {
|
||||
frappe.model.set_value(row.doctype, row.name, "cost_center", r.message);
|
||||
msgprint(__("Cost Center For Item with Item Code '"+row.item_name+"' has been Changed to "+ r.message));
|
||||
})
|
||||
if(in_list(["Delivery Note", "Sales Invoice"], frm.doc.doctype)) {
|
||||
frappe.call({
|
||||
method:'erpnext.projects.doctype.project.project.get_cost_center_name' ,
|
||||
args: { project_name: frm.doc.project_name },
|
||||
callback: function(r, rt) {
|
||||
if(!r.exc) {
|
||||
$.each(frm.doc[cur_frm.cscript.fname] || [], function(i, row) {
|
||||
frappe.model.set_value(row.doctype, row.name, "cost_center", r.message);
|
||||
msgprint(__("Cost Center For Item with Item Code '"+row.item_name+"' has been Changed to "+ r.message));
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -71,16 +71,17 @@ def setup_account(args=None):
|
||||
|
||||
frappe.db.set_default('desktop:home_page', 'desktop')
|
||||
|
||||
website_maker(args.company_name, args.company_tagline, args.name)
|
||||
website_maker(args.company_name.strip(), args.company_tagline, args.name)
|
||||
create_logo(args)
|
||||
|
||||
frappe.clear_cache()
|
||||
frappe.db.commit()
|
||||
|
||||
except:
|
||||
traceback = frappe.get_traceback()
|
||||
for hook in frappe.get_hooks("setup_wizard_exception"):
|
||||
frappe.get_attr(hook)(traceback, args)
|
||||
if args:
|
||||
traceback = frappe.get_traceback()
|
||||
for hook in frappe.get_hooks("setup_wizard_exception"):
|
||||
frappe.get_attr(hook)(traceback, args)
|
||||
|
||||
raise
|
||||
|
||||
@@ -134,7 +135,7 @@ def create_fiscal_year_and_company(args):
|
||||
frappe.get_doc({
|
||||
"doctype":"Company",
|
||||
'domain': args.get("industry"),
|
||||
'company_name':args.get('company_name'),
|
||||
'company_name':args.get('company_name').strip(),
|
||||
'abbr':args.get('company_abbr'),
|
||||
'default_currency':args.get('currency'),
|
||||
'country': args.get('country'),
|
||||
@@ -165,7 +166,7 @@ def set_defaults(args):
|
||||
global_defaults.update({
|
||||
'current_fiscal_year': args.curr_fiscal_year,
|
||||
'default_currency': args.get('currency'),
|
||||
'default_company':args.get('company_name'),
|
||||
'default_company':args.get('company_name').strip(),
|
||||
"country": args.get("country"),
|
||||
})
|
||||
|
||||
@@ -284,7 +285,7 @@ def create_taxes(args):
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype":"Account",
|
||||
"company": args.get("company_name"),
|
||||
"company": args.get("company_name").strip(),
|
||||
"parent_account": _("Duties and Taxes") + " - " + args.get("company_abbr"),
|
||||
"account_name": args.get("tax_" + str(i)),
|
||||
"group_or_ledger": "Ledger",
|
||||
@@ -344,7 +345,7 @@ def create_customers(args):
|
||||
"customer_type": "Company",
|
||||
"customer_group": _("Commercial"),
|
||||
"territory": args.get("country"),
|
||||
"company": args.get("company_name")
|
||||
"company": args.get("company_name").strip()
|
||||
}).insert()
|
||||
|
||||
if args.get("customer_contact_" + str(i)):
|
||||
@@ -364,7 +365,7 @@ def create_suppliers(args):
|
||||
"doctype":"Supplier",
|
||||
"supplier_name": supplier,
|
||||
"supplier_type": _("Local"),
|
||||
"company": args.get("company_name")
|
||||
"company": args.get("company_name").strip()
|
||||
}).insert()
|
||||
|
||||
if args.get("supplier_contact_" + str(i)):
|
||||
|
||||
@@ -78,7 +78,8 @@ data_map = {
|
||||
"Stock Ledger Entry": {
|
||||
"columns": ["name", "posting_date", "posting_time", "item_code", "warehouse",
|
||||
"actual_qty as qty", "voucher_type", "voucher_no", "project",
|
||||
"ifnull(incoming_rate,0) as incoming_rate", "stock_uom", "serial_no"],
|
||||
"ifnull(incoming_rate,0) as incoming_rate", "stock_uom", "serial_no",
|
||||
"qty_after_transaction", "valuation_rate"],
|
||||
"order_by": "posting_date, posting_time, name",
|
||||
"links": {
|
||||
"item_code": ["Item", "name"],
|
||||
|
||||
@@ -3,8 +3,15 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Batch(Document):
|
||||
pass
|
||||
|
||||
def validate(self):
|
||||
self.item_has_batch_enabled()
|
||||
|
||||
def item_has_batch_enabled(self):
|
||||
has_batch_no = frappe.db.get_value("Item",self.item,"has_batch_no")
|
||||
if has_batch_no =='No':
|
||||
frappe.throw(_("The selected item cannot have Batch"))
|
||||
14
erpnext/stock/doctype/batch/test_batch.py
Normal file
14
erpnext/stock/doctype/batch/test_batch.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.exceptions import ValidationError
|
||||
import unittest
|
||||
|
||||
class TestBatch(unittest.TestCase):
|
||||
def test_item_has_batch_enabled(self):
|
||||
self.assertRaises(ValidationError, frappe.get_doc({
|
||||
"doctype": "Batch",
|
||||
"name": "_test Batch",
|
||||
"item": "_Test Item"
|
||||
}).save)
|
||||
@@ -11,27 +11,27 @@ class Bin(Document):
|
||||
def validate(self):
|
||||
if self.get("__islocal") or not self.stock_uom:
|
||||
self.stock_uom = frappe.db.get_value('Item', self.item_code, 'stock_uom')
|
||||
|
||||
|
||||
self.validate_mandatory()
|
||||
|
||||
|
||||
self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \
|
||||
flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
||||
|
||||
|
||||
def validate_mandatory(self):
|
||||
qf = ['actual_qty', 'reserved_qty', 'ordered_qty', 'indented_qty']
|
||||
for f in qf:
|
||||
if (not getattr(self, f, None)) or (not self.get(f)):
|
||||
if (not getattr(self, f, None)) or (not self.get(f)):
|
||||
self.set(f, 0.0)
|
||||
|
||||
|
||||
def update_stock(self, args):
|
||||
self.update_qty(args)
|
||||
|
||||
if args.get("actual_qty"):
|
||||
|
||||
if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
|
||||
from erpnext.stock.stock_ledger import update_entries_after
|
||||
|
||||
|
||||
if not args.get("posting_date"):
|
||||
args["posting_date"] = nowdate()
|
||||
|
||||
|
||||
# update valuation and qty after transaction for post dated entry
|
||||
update_entries_after({
|
||||
"item_code": self.item_code,
|
||||
@@ -39,21 +39,34 @@ class Bin(Document):
|
||||
"posting_date": args.get("posting_date"),
|
||||
"posting_time": args.get("posting_time")
|
||||
})
|
||||
|
||||
|
||||
def update_qty(self, args):
|
||||
# update the stock values (for current quantities)
|
||||
|
||||
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
|
||||
if args.get("voucher_type")=="Stock Reconciliation":
|
||||
if args.get('is_cancelled') == 'No':
|
||||
self.actual_qty = args.get("qty_after_transaction")
|
||||
else:
|
||||
qty_after_transaction = frappe.db.get_value("""select qty_after_transaction
|
||||
from `tabStock Ledger Entry`
|
||||
where item_code=%s and warehouse=%s
|
||||
and not (voucher_type='Stock Reconciliation' and voucher_no=%s)
|
||||
order by posting_date desc limit 1""",
|
||||
(self.item_code, self.warehouse, args.get('voucher_no')))
|
||||
|
||||
self.actual_qty = flt(qty_after_transaction[0][0]) if qty_after_transaction else 0.0
|
||||
else:
|
||||
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
|
||||
|
||||
self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
|
||||
self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
|
||||
self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
|
||||
self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
|
||||
|
||||
|
||||
self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \
|
||||
flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
||||
|
||||
|
||||
self.save()
|
||||
|
||||
|
||||
def get_first_sle(self):
|
||||
sle = frappe.db.sql("""
|
||||
select * from `tabStock Ledger Entry`
|
||||
@@ -62,4 +75,4 @@ class Bin(Document):
|
||||
order by timestamp(posting_date, posting_time) asc, name asc
|
||||
limit 1
|
||||
""", (self.item_code, self.warehouse), as_dict=1)
|
||||
return sle and sle[0] or None
|
||||
return sle and sle[0] or None
|
||||
|
||||
@@ -52,11 +52,15 @@ class DeliveryNote(SellingController):
|
||||
else:
|
||||
df.delete_key("__print_hide")
|
||||
|
||||
toggle_print_hide(self.meta, "currency")
|
||||
|
||||
item_meta = frappe.get_meta("Delivery Note Item")
|
||||
for fieldname in ("rate", "amount", "price_list_rate", "discount_percentage"):
|
||||
toggle_print_hide(item_meta, fieldname)
|
||||
print_hide_fields = {
|
||||
"parent": ["grand_total_export", "rounded_total_export", "in_words_export", "currency", "net_total_export"],
|
||||
"items": ["rate", "amount", "price_list_rate", "discount_percentage"]
|
||||
}
|
||||
|
||||
for key, fieldname in print_hide_fields.items():
|
||||
for f in fieldname:
|
||||
toggle_print_hide(self.meta if key == "parent" else item_meta, f)
|
||||
|
||||
def get_portal_page(self):
|
||||
return "shipment" if self.docstatus==1 else None
|
||||
@@ -245,7 +249,7 @@ class DeliveryNote(SellingController):
|
||||
sl_entries = []
|
||||
for d in self.get_item_list():
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
|
||||
and d.warehouse:
|
||||
and d.warehouse and flt(d['qty']):
|
||||
self.update_reserved_qty(d)
|
||||
|
||||
sl_entries.append(self.get_sl_entries(d, {
|
||||
|
||||
@@ -19,7 +19,7 @@ cur_frm.cscript.refresh = function(doc) {
|
||||
cur_frm.cscript.edit_prices_button();
|
||||
|
||||
if (!doc.__islocal && doc.is_stock_item == 'Yes') {
|
||||
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method'],
|
||||
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method', 'has_batch_no'],
|
||||
(doc.__onload && doc.__onload.sle_exists=="exists") ? false : true);
|
||||
}
|
||||
|
||||
@@ -185,4 +185,4 @@ cur_frm.cscript.image = function() {
|
||||
else {
|
||||
msgprint(__("You may need to update: {0}", [frappe.meta.get_docfield(cur_frm.doc.doctype, "description_html").label]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,13 +187,14 @@ class Item(WebsiteGenerator):
|
||||
def cant_change(self):
|
||||
if not self.get("__islocal"):
|
||||
vals = frappe.db.get_value("Item", self.name,
|
||||
["has_serial_no", "is_stock_item", "valuation_method"], as_dict=True)
|
||||
["has_serial_no", "is_stock_item", "valuation_method", "has_batch_no"], as_dict=True)
|
||||
|
||||
if vals and ((self.is_stock_item == "No" and vals.is_stock_item == "Yes") or
|
||||
vals.has_serial_no != self.has_serial_no or
|
||||
vals.has_batch_no != self.has_batch_no or
|
||||
cstr(vals.valuation_method) != cstr(self.valuation_method)):
|
||||
if self.check_if_sle_exists() == "exists":
|
||||
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Is Stock Item' and 'Valuation Method'"))
|
||||
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Has Batch No', 'Is Stock Item' and 'Valuation Method'"))
|
||||
|
||||
def validate_item_type_for_reorder(self):
|
||||
if self.re_order_level or len(self.get("item_reorder", {"material_request_type": "Purchase"})):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
frappe.listview_settings['Item'] = {
|
||||
add_fields: ["item_name", "stock_uom", "item_group", "image",
|
||||
"is_stock_item", "is_sales_item", "is_purchase_item",
|
||||
"is_manufactured_item", "show_in_website"]
|
||||
add_fields: ["`tabItem`.`item_name`", "`tabItem`.`stock_uom`", "`tabItem`.`item_group`", "`tabItem`.`image`",
|
||||
"`tabItem`.`is_stock_item`", "`tabItem`.`is_sales_item`", "`tabItem`.`is_purchase_item`",
|
||||
"`tabItem`.`is_manufactured_item`", "`tabItem`.`show_in_website`"]
|
||||
};
|
||||
|
||||
@@ -9,6 +9,6 @@ class TestItem(unittest.TestCase):
|
||||
def test_duplicate_item(self):
|
||||
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
|
||||
doc = frappe.copy_doc(test_records[0])
|
||||
self.assertRaises(ItemPriceDuplicateItem, doc.insert)
|
||||
self.assertRaises(ItemPriceDuplicateItem, doc.save)
|
||||
|
||||
test_records = frappe.get_test_records('Item Price')
|
||||
@@ -16,5 +16,11 @@
|
||||
"item_code": "_Test Item 2",
|
||||
"price_list": "_Test Price List Rest of the World",
|
||||
"price_list_rate": 20
|
||||
},
|
||||
{
|
||||
"doctype": "Item Price",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"price_list": "_Test Price List",
|
||||
"price_list_rate": 1000
|
||||
}
|
||||
]
|
||||
|
||||
@@ -97,10 +97,10 @@ class LandedCostVoucher(Document):
|
||||
|
||||
# update stock & gl entries for cancelled state of PR
|
||||
pr.docstatus = 2
|
||||
pr.update_stock()
|
||||
pr.update_stock_ledger()
|
||||
pr.make_gl_entries_on_cancel()
|
||||
|
||||
# update stock & gl entries for submit state of PR
|
||||
pr.docstatus = 1
|
||||
pr.update_stock()
|
||||
pr.update_stock_ledger()
|
||||
pr.make_gl_entries()
|
||||
|
||||
@@ -79,30 +79,9 @@ class MaterialRequest(BuyingController):
|
||||
# NOTE: Since Item BOM and FG quantities are combined, using current data, it cannot be validated
|
||||
# Though the creation of Material Request from a Production Plan can be rethought to fix this
|
||||
|
||||
def update_bin(self, is_submit, is_stopped):
|
||||
""" Update Quantity Requested for Purchase in Bin for Material Request of type 'Purchase'"""
|
||||
|
||||
from erpnext.stock.utils import update_bin
|
||||
for d in self.get('indent_details'):
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes":
|
||||
if not d.warehouse:
|
||||
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
|
||||
|
||||
qty =flt(d.qty)
|
||||
if is_stopped:
|
||||
qty = (d.qty > d.ordered_qty) and flt(flt(d.qty) - flt(d.ordered_qty)) or 0
|
||||
|
||||
args = {
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.warehouse,
|
||||
"indented_qty": (is_submit and 1 or -1) * flt(qty),
|
||||
"posting_date": self.transaction_date
|
||||
}
|
||||
update_bin(args)
|
||||
|
||||
def on_submit(self):
|
||||
frappe.db.set(self, 'status', 'Submitted')
|
||||
self.update_bin(is_submit = 1, is_stopped = 0)
|
||||
self.update_requested_qty()
|
||||
|
||||
def check_modified_date(self):
|
||||
mod_db = frappe.db.sql("""select modified from `tabMaterial Request` where name = %s""",
|
||||
@@ -115,23 +94,18 @@ class MaterialRequest(BuyingController):
|
||||
|
||||
def update_status(self, status):
|
||||
self.check_modified_date()
|
||||
self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1)
|
||||
self.update_requested_qty()
|
||||
frappe.db.set(self, 'status', cstr(status))
|
||||
frappe.msgprint(_("Status updated to {0}").format(_(status)))
|
||||
|
||||
def on_cancel(self):
|
||||
# Step 1:=> Get Purchase Common Obj
|
||||
pc_obj = frappe.get_doc('Purchase Common')
|
||||
|
||||
# Step 2:=> Check for stopped status
|
||||
pc_obj.check_for_stopped_status(self.doctype, self.name)
|
||||
|
||||
# Step 3:=> Check if Purchase Order has been submitted against current Material Request
|
||||
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Order', docname = self.name, detail_doctype = 'Purchase Order Item')
|
||||
# Step 4:=> Update Bin
|
||||
self.update_bin(is_submit = 0, is_stopped = (cstr(self.status) == 'Stopped') and 1 or 0)
|
||||
|
||||
# Step 5:=> Set Status
|
||||
self.update_requested_qty()
|
||||
|
||||
frappe.db.set(self,'status','Cancelled')
|
||||
|
||||
def update_completed_qty(self, mr_items=None):
|
||||
@@ -162,56 +136,47 @@ class MaterialRequest(BuyingController):
|
||||
self.per_ordered = flt((per_ordered / flt(len(item_doclist))) * 100.0, 2)
|
||||
frappe.db.set_value(self.doctype, self.name, "per_ordered", self.per_ordered)
|
||||
|
||||
def update_completed_qty(doc, method):
|
||||
if doc.doctype == "Stock Entry":
|
||||
def update_requested_qty(self, mr_item_rows=None):
|
||||
"""update requested qty (before ordered_qty is updated)"""
|
||||
from erpnext.stock.utils import get_bin
|
||||
|
||||
def _update_requested_qty(item_code, warehouse):
|
||||
requested_qty = frappe.db.sql("""select sum(mr_item.qty - ifnull(mr_item.ordered_qty, 0))
|
||||
from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
|
||||
where mr_item.item_code=%s and mr_item.warehouse=%s
|
||||
and mr_item.qty > ifnull(mr_item.ordered_qty, 0) and mr_item.parent=mr.name
|
||||
and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse))
|
||||
|
||||
bin_doc = get_bin(item_code, warehouse)
|
||||
bin_doc.indented_qty = flt(requested_qty[0][0]) if requested_qty else 0
|
||||
bin_doc.save()
|
||||
|
||||
item_wh_list = []
|
||||
for d in self.get("indent_details"):
|
||||
if (not mr_item_rows or d.name in mr_item_rows) and [d.item_code, d.warehouse] not in item_wh_list \
|
||||
and frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" and d.warehouse:
|
||||
item_wh_list.append([d.item_code, d.warehouse])
|
||||
|
||||
for item_code, warehouse in item_wh_list:
|
||||
_update_requested_qty(item_code, warehouse)
|
||||
|
||||
def update_completed_and_requested_qty(stock_entry, method):
|
||||
if stock_entry.doctype == "Stock Entry":
|
||||
material_request_map = {}
|
||||
|
||||
for d in doc.get("mtn_details"):
|
||||
for d in stock_entry.get("mtn_details"):
|
||||
if d.material_request:
|
||||
material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
|
||||
|
||||
for mr_name, mr_items in material_request_map.items():
|
||||
mr_obj = frappe.get_doc("Material Request", mr_name)
|
||||
for mr, mr_item_rows in material_request_map.items():
|
||||
if mr and mr_item_rows:
|
||||
mr_obj = frappe.get_doc("Material Request", mr)
|
||||
|
||||
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||
frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr_obj.name),
|
||||
frappe.InvalidStatusError)
|
||||
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||
frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError)
|
||||
|
||||
_update_requested_qty(doc, mr_obj, mr_items)
|
||||
|
||||
# update ordered percentage and qty
|
||||
mr_obj.update_completed_qty(mr_items)
|
||||
|
||||
def _update_requested_qty(doc, mr_obj, mr_items):
|
||||
"""update requested qty (before ordered_qty is updated)"""
|
||||
from erpnext.stock.utils import update_bin
|
||||
for mr_item_name in mr_items:
|
||||
mr_item = mr_obj.get("indent_details", {"name": mr_item_name})
|
||||
se_detail = doc.get("mtn_details", {"material_request": mr_obj.name,
|
||||
"material_request_item": mr_item_name})
|
||||
|
||||
if mr_item and se_detail:
|
||||
mr_item = mr_item[0]
|
||||
se_detail = se_detail[0]
|
||||
mr_item.ordered_qty = flt(mr_item.ordered_qty)
|
||||
mr_item.qty = flt(mr_item.qty)
|
||||
se_detail.transfer_qty = flt(se_detail.transfer_qty)
|
||||
|
||||
if se_detail.docstatus == 2 and mr_item.ordered_qty > mr_item.qty \
|
||||
and se_detail.transfer_qty == mr_item.ordered_qty:
|
||||
add_indented_qty = mr_item.qty
|
||||
elif se_detail.docstatus == 1 and \
|
||||
mr_item.ordered_qty + se_detail.transfer_qty > mr_item.qty:
|
||||
add_indented_qty = mr_item.qty - mr_item.ordered_qty
|
||||
else:
|
||||
add_indented_qty = se_detail.transfer_qty
|
||||
|
||||
update_bin({
|
||||
"item_code": se_detail.item_code,
|
||||
"warehouse": se_detail.t_warehouse,
|
||||
"indented_qty": (se_detail.docstatus==2 and 1 or -1) * add_indented_qty,
|
||||
"posting_date": doc.posting_date,
|
||||
})
|
||||
mr_obj.update_completed_qty(mr_item_rows)
|
||||
mr_obj.update_requested_qty(mr_item_rows)
|
||||
|
||||
def set_missing_values(source, target_doc):
|
||||
target_doc.run_method("set_missing_values")
|
||||
|
||||
@@ -58,12 +58,6 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(se.doctype, "Stock Entry")
|
||||
self.assertEquals(len(se.get("mtn_details")), len(mr.get("indent_details")))
|
||||
|
||||
def _test_requested_qty(self, qty1, qty2):
|
||||
self.assertEqual(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "indented_qty")), qty1)
|
||||
self.assertEqual(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 200",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "indented_qty")), qty2)
|
||||
|
||||
def _insert_stock_entry(self, qty1, qty2):
|
||||
se = frappe.get_doc({
|
||||
"company": "_Test Company",
|
||||
@@ -103,7 +97,8 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
se.submit()
|
||||
|
||||
def test_completed_qty_for_purchase(self):
|
||||
frappe.db.sql("""delete from `tabBin`""")
|
||||
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
# submit material request of type Purchase
|
||||
mr = frappe.copy_doc(test_records[0])
|
||||
@@ -115,8 +110,6 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
|
||||
# map a purchase order
|
||||
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
|
||||
po_doc = make_purchase_order(mr.name)
|
||||
@@ -149,7 +142,12 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.per_ordered, 50)
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
||||
self._test_requested_qty(27.0, 1.5)
|
||||
|
||||
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
|
||||
|
||||
po.cancel()
|
||||
# check if per complete is as expected
|
||||
@@ -158,11 +156,15 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, None)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, None)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||
|
||||
def test_completed_qty_for_transfer(self):
|
||||
frappe.db.sql("""delete from `tabBin`""")
|
||||
frappe.db.sql("""delete from `tabStock Ledger Entry`""")
|
||||
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
# submit material request of type Purchase
|
||||
mr = frappe.copy_doc(test_records[0])
|
||||
@@ -175,7 +177,11 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||
|
||||
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
|
||||
|
||||
@@ -226,7 +232,11 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
||||
|
||||
self._test_requested_qty(27.0, 1.5)
|
||||
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
|
||||
|
||||
# check if per complete is as expected for Stock Entry cancelled
|
||||
se.cancel()
|
||||
@@ -235,11 +245,15 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||
|
||||
def test_completed_qty_for_over_transfer(self):
|
||||
frappe.db.sql("""delete from `tabBin`""")
|
||||
frappe.db.sql("""delete from `tabStock Ledger Entry`""")
|
||||
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
# submit material request of type Purchase
|
||||
mr = frappe.copy_doc(test_records[0])
|
||||
@@ -252,8 +266,6 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
|
||||
# map a stock entry
|
||||
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
|
||||
|
||||
@@ -297,7 +309,12 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.per_ordered, 100)
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 60.0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 3.0)
|
||||
self._test_requested_qty(0.0, 0.0)
|
||||
|
||||
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2)
|
||||
|
||||
# check if per complete is as expected for Stock Entry cancelled
|
||||
se.cancel()
|
||||
@@ -306,7 +323,11 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||
|
||||
def test_incorrect_mapping_of_stock_entry(self):
|
||||
# submit material request of type Purchase
|
||||
@@ -348,5 +369,9 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
mr.company = "_Test Company 1"
|
||||
self.assertRaises(InvalidWarehouseCompany, mr.insert)
|
||||
|
||||
def _get_requested_qty(self, item_code, warehouse):
|
||||
return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty"))
|
||||
|
||||
|
||||
test_dependencies = ["Currency Exchange"]
|
||||
test_records = frappe.get_test_records('Material Request')
|
||||
|
||||
@@ -162,8 +162,7 @@ def item_details(doctype, txt, searchfield, start, page_len, filters):
|
||||
from erpnext.controllers.queries import get_match_cond
|
||||
return frappe.db.sql("""select name, item_name, description from `tabItem`
|
||||
where name in ( select item_code FROM `tabDelivery Note Item`
|
||||
where parent= %s
|
||||
and ifnull(qty, 0) > ifnull(packed_qty, 0))
|
||||
where parent= %s)
|
||||
and %s like "%s" %s
|
||||
limit %s, %s """ % ("%s", searchfield, "%s",
|
||||
get_match_cond(doctype), "%s", "%s"),
|
||||
|
||||
@@ -8,7 +8,6 @@ from frappe.utils import cstr, flt, cint
|
||||
|
||||
from frappe import _
|
||||
import frappe.defaults
|
||||
from erpnext.stock.utils import update_bin
|
||||
|
||||
from erpnext.controllers.buying_controller import BuyingController
|
||||
|
||||
@@ -130,7 +129,7 @@ class PurchaseReceipt(BuyingController):
|
||||
if not d.prevdoc_docname:
|
||||
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
||||
|
||||
def update_stock(self):
|
||||
def update_stock_ledger(self):
|
||||
sl_entries = []
|
||||
stock_items = self.get_stock_items()
|
||||
|
||||
@@ -157,29 +156,19 @@ class PurchaseReceipt(BuyingController):
|
||||
self.make_sl_entries(sl_entries)
|
||||
|
||||
def update_ordered_qty(self):
|
||||
stock_items = self.get_stock_items()
|
||||
po_map = {}
|
||||
for d in self.get("purchase_receipt_details"):
|
||||
if d.item_code in stock_items and d.warehouse \
|
||||
and cstr(d.prevdoc_doctype) == 'Purchase Order':
|
||||
if d.prevdoc_doctype and d.prevdoc_doctype == "Purchase Order" and d.prevdoc_detail_docname:
|
||||
po_map.setdefault(d.prevdoc_docname, []).append(d.prevdoc_detail_docname)
|
||||
|
||||
already_received_qty = self.get_already_received_qty(d.prevdoc_docname,
|
||||
d.prevdoc_detail_docname)
|
||||
po_qty, ordered_warehouse = self.get_po_qty_and_warehouse(d.prevdoc_detail_docname)
|
||||
for po, po_item_rows in po_map.items():
|
||||
if po and po_item_rows:
|
||||
po_obj = frappe.get_doc("Purchase Order", po)
|
||||
|
||||
if not ordered_warehouse:
|
||||
frappe.throw(_("Warehouse is missing in Purchase Order"))
|
||||
if po_obj.status in ["Stopped", "Cancelled"]:
|
||||
frappe.throw(_("Material Request {0} is cancelled or stopped").format(po), frappe.InvalidStatusError)
|
||||
|
||||
if already_received_qty + d.qty > po_qty:
|
||||
ordered_qty = - (po_qty - already_received_qty) * flt(d.conversion_factor)
|
||||
else:
|
||||
ordered_qty = - flt(d.qty) * flt(d.conversion_factor)
|
||||
|
||||
update_bin({
|
||||
"item_code": d.item_code,
|
||||
"warehouse": ordered_warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"ordered_qty": flt(ordered_qty) if self.docstatus==1 else -flt(ordered_qty)
|
||||
})
|
||||
po_obj.update_ordered_qty(po_item_rows)
|
||||
|
||||
def get_already_received_qty(self, po, po_detail):
|
||||
qty = frappe.db.sql("""select sum(qty) from `tabPurchase Receipt Item`
|
||||
@@ -234,7 +223,7 @@ class PurchaseReceipt(BuyingController):
|
||||
|
||||
self.update_ordered_qty()
|
||||
|
||||
self.update_stock()
|
||||
self.update_stock_ledger()
|
||||
|
||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
||||
update_serial_nos_after_submit(self, "purchase_receipt_details")
|
||||
@@ -265,11 +254,13 @@ class PurchaseReceipt(BuyingController):
|
||||
|
||||
frappe.db.set(self,'status','Cancelled')
|
||||
|
||||
self.update_ordered_qty()
|
||||
|
||||
self.update_stock()
|
||||
self.update_stock_ledger()
|
||||
|
||||
self.update_prevdoc_status()
|
||||
|
||||
# Must be called after updating received qty in PO
|
||||
self.update_ordered_qty()
|
||||
|
||||
pc_obj.update_last_purchase_rate(self, 0)
|
||||
|
||||
self.make_gl_entries_on_cancel()
|
||||
|
||||
@@ -95,7 +95,7 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
pr.insert()
|
||||
|
||||
self.assertEquals(len(pr.get("pr_raw_material_details")), 2)
|
||||
self.assertEquals(pr.get("purchase_receipt_details")[0].rm_supp_cost, 70000.0)
|
||||
self.assertEquals(pr.get("purchase_receipt_details")[0].rm_supp_cost, 20750.0)
|
||||
|
||||
|
||||
def test_serial_no_supplier(self):
|
||||
@@ -151,6 +151,6 @@ def set_perpetual_inventory(enable=1):
|
||||
accounts_settings.save()
|
||||
|
||||
|
||||
test_dependencies = ["BOM"]
|
||||
test_dependencies = ["BOM", "Item Price"]
|
||||
|
||||
test_records = frappe.get_test_records('Purchase Receipt')
|
||||
|
||||
@@ -120,7 +120,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
clean_up: function() {
|
||||
// Clear Production Order record from locals, because it is updated via Stock Entry
|
||||
if(this.frm.doc.production_order &&
|
||||
this.frm.doc.purpose == "Manufacture/Repack") {
|
||||
this.frm.doc.purpose == "Manufacture") {
|
||||
frappe.model.remove_from_locals("Production Order",
|
||||
this.frm.doc.production_order);
|
||||
}
|
||||
@@ -162,7 +162,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
},
|
||||
|
||||
toggle_enable_bom: function() {
|
||||
this.frm.toggle_enable("bom_no", !this.frm.doc.production_order);
|
||||
this.frm.toggle_enable("bom_no", this.frm.doc.purpose!="Manufacture");
|
||||
},
|
||||
|
||||
get_doctype_docname: function() {
|
||||
@@ -339,6 +339,8 @@ cur_frm.cscript.toggle_related_fields = function(doc) {
|
||||
cur_frm.fields_dict["mtn_details"].grid.set_column_disp("s_warehouse", !disable_from_warehouse);
|
||||
cur_frm.fields_dict["mtn_details"].grid.set_column_disp("t_warehouse", !disable_to_warehouse);
|
||||
|
||||
cur_frm.cscript.toggle_enable_bom();
|
||||
|
||||
if(doc.purpose == 'Purchase Return') {
|
||||
doc.customer = doc.customer_name = doc.customer_address =
|
||||
doc.delivery_note_no = doc.sales_invoice_no = null;
|
||||
@@ -351,6 +353,8 @@ cur_frm.cscript.toggle_related_fields = function(doc) {
|
||||
doc.delivery_note_no = doc.sales_invoice_no = doc.supplier =
|
||||
doc.supplier_name = doc.supplier_address = doc.purchase_receipt_no = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['production_order'].get_query = function(doc) {
|
||||
@@ -457,4 +461,5 @@ cur_frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
|
||||
cur_frm.fields_dict.supplier.get_query = function(doc, cdt, cdn) {
|
||||
return { query: "erpnext.controllers.queries.supplier_query" }
|
||||
}
|
||||
cur_frm.add_fetch('production_order', 'total_fixed_cost', 'total_fixed_cost');
|
||||
cur_frm.add_fetch('production_order', 'total_fixed_cost', 'total_fixed_cost');
|
||||
cur_frm.add_fetch('bom_no', 'total_fixed_cost', 'total_fixed_cost');
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@ class StockEntry(StockController):
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
self.validate_uom_is_integer("stock_uom", "transfer_qty")
|
||||
self.validate_warehouse(pro_obj)
|
||||
self.validate_production_order(pro_obj)
|
||||
self.validate_production_order()
|
||||
self.get_stock_and_rate()
|
||||
self.validate_incoming_rate()
|
||||
self.validate_bom()
|
||||
@@ -54,6 +54,7 @@ class StockEntry(StockController):
|
||||
self.validate_valuation_rate()
|
||||
self.set_total_amount()
|
||||
|
||||
|
||||
def on_submit(self):
|
||||
self.update_stock_ledger()
|
||||
|
||||
@@ -74,7 +75,7 @@ class StockEntry(StockController):
|
||||
|
||||
def validate_purpose(self):
|
||||
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
|
||||
"Manufacture/Repack", "Subcontract", "Sales Return", "Purchase Return"]
|
||||
"Manufacture", "Repack", "Subcontract", "Sales Return", "Purchase Return"]
|
||||
if self.purpose not in valid_purposes:
|
||||
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
|
||||
|
||||
@@ -137,7 +138,7 @@ class StockEntry(StockController):
|
||||
if self.purpose in target_mandatory and not d.t_warehouse:
|
||||
frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
|
||||
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
if self.purpose in ["Manufacture", "Repack"]:
|
||||
if validate_for_manufacture_repack:
|
||||
if d.bom_no:
|
||||
d.s_warehouse = None
|
||||
@@ -156,14 +157,11 @@ class StockEntry(StockController):
|
||||
if cstr(d.s_warehouse) == cstr(d.t_warehouse):
|
||||
frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
|
||||
|
||||
def validate_production_order(self, pro_obj=None):
|
||||
if not pro_obj:
|
||||
if self.production_order:
|
||||
pro_obj = frappe.get_doc('Production Order', self.production_order)
|
||||
else:
|
||||
return
|
||||
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
def validate_production_order(self):
|
||||
if self.purpose == "Manufacture":
|
||||
# check if production order is entered
|
||||
if not self.production_order:
|
||||
frappe.throw(_("Production order number is mandatory for stock entry purpose manufacture"))
|
||||
# check for double entry
|
||||
self.check_duplicate_entry_for_production_order()
|
||||
elif self.purpose != "Material Transfer":
|
||||
@@ -192,7 +190,7 @@ class StockEntry(StockController):
|
||||
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
|
||||
|
||||
def validate_valuation_rate(self):
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
if self.purpose in ["Manufacture", "Repack"]:
|
||||
valuation_at_source, valuation_at_target = 0, 0
|
||||
for d in self.get("mtn_details"):
|
||||
if d.s_warehouse and not d.t_warehouse:
|
||||
@@ -248,7 +246,7 @@ class StockEntry(StockController):
|
||||
raw_material_cost += flt(d.amount)
|
||||
|
||||
# set incoming rate for fg item
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
if self.purpose in ["Manufacture", "Repack"]:
|
||||
number_of_fg_items = len([t.t_warehouse for t in self.get("mtn_details") if t.t_warehouse])
|
||||
for d in self.get("mtn_details"):
|
||||
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
|
||||
@@ -391,7 +389,7 @@ class StockEntry(StockController):
|
||||
pro_doc = frappe.get_doc("Production Order", self.production_order)
|
||||
_validate_production_order(pro_doc)
|
||||
pro_doc.run_method("update_status")
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
if self.purpose == "Manufacture":
|
||||
pro_doc.run_method("update_produced_qty")
|
||||
self.update_planned_qty(pro_doc)
|
||||
|
||||
@@ -463,20 +461,20 @@ class StockEntry(StockController):
|
||||
|
||||
def get_items(self):
|
||||
self.set('mtn_details', [])
|
||||
self.validate_production_order()
|
||||
|
||||
pro_obj = None
|
||||
if self.production_order:
|
||||
# common validations
|
||||
pro_obj = frappe.get_doc('Production Order', self.production_order)
|
||||
if pro_obj:
|
||||
self.validate_production_order(pro_obj)
|
||||
self.bom_no = pro_obj.bom_no
|
||||
else:
|
||||
# invalid production order
|
||||
self.production_order = None
|
||||
|
||||
if self.bom_no:
|
||||
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture/Repack",
|
||||
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
|
||||
"Subcontract"]:
|
||||
if self.production_order and self.purpose == "Material Transfer":
|
||||
item_dict = self.get_pending_raw_materials(pro_obj)
|
||||
@@ -493,7 +491,7 @@ class StockEntry(StockController):
|
||||
self.add_to_stock_entry_detail(item_dict)
|
||||
|
||||
# add finished good item to Stock Entry Detail table -- along with bom_no
|
||||
if self.production_order and self.purpose == "Manufacture/Repack":
|
||||
if self.production_order and self.purpose == "Manufacture":
|
||||
item = frappe.db.get_value("Item", pro_obj.production_item, ["item_name",
|
||||
"description", "stock_uom", "expense_account", "buying_cost_center"], as_dict=1)
|
||||
self.add_to_stock_entry_detail({
|
||||
@@ -509,7 +507,7 @@ class StockEntry(StockController):
|
||||
}
|
||||
}, bom_no=pro_obj.bom_no)
|
||||
|
||||
elif self.purpose in ["Material Receipt", "Manufacture/Repack"]:
|
||||
elif self.purpose in ["Material Receipt", "Repack"]:
|
||||
if self.purpose=="Material Receipt":
|
||||
self.from_warehouse = ""
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"Material Issue": "icon-arrow-right",
|
||||
"Material Receipt": "icon-arrow-left",
|
||||
"Material Transfer": "icon-resize-horizontal",
|
||||
"Manufacture/Repack": "icon-wrench",
|
||||
"Manufacture": "icon-wrench",
|
||||
"Repack": "icon-wrench",
|
||||
"Sales Return": "icon-warning-sign",
|
||||
"Purchase Return": "icon-warning-sign",
|
||||
"Subcontract": "icon-truck"
|
||||
|
||||
@@ -108,6 +108,6 @@
|
||||
],
|
||||
"posting_date": "2013-01-25",
|
||||
"posting_time": "17:14:24",
|
||||
"purpose": "Manufacture/Repack"
|
||||
"purpose": "Repack"
|
||||
}
|
||||
]
|
||||
@@ -9,14 +9,64 @@ from erpnext.stock.doctype.serial_no.serial_no import *
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
|
||||
|
||||
def get_sle(**args):
|
||||
condition, values = "", []
|
||||
for key, value in args.iteritems():
|
||||
condition += " and " if condition else " where "
|
||||
condition += "`{0}`=%s".format(key)
|
||||
values.append(value)
|
||||
|
||||
return frappe.db.sql("""select * from `tabStock Ledger Entry` %s
|
||||
order by timestamp(posting_date, posting_time) desc, name desc limit 1"""% condition,
|
||||
values, as_dict=1)
|
||||
|
||||
def make_zero(item_code, warehouse):
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)
|
||||
qty = sle[0].qty_after_transaction if sle else 0
|
||||
if qty < 0:
|
||||
make_stock_entry(item_code, None, warehouse, abs(qty), incoming_rate=10)
|
||||
elif qty > 0:
|
||||
make_stock_entry(item_code, warehouse, None, qty, incoming_rate=10)
|
||||
|
||||
class TestStockEntry(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
frappe.set_user("Administrator")
|
||||
set_perpetual_inventory(0)
|
||||
if hasattr(self, "old_default_company"):
|
||||
frappe.db.set_default("company", self.old_default_company)
|
||||
|
||||
def test_fifo(self):
|
||||
frappe.db.set_default("allow_negative_stock", 1)
|
||||
item_code = "_Test Item 2"
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
make_zero(item_code, warehouse)
|
||||
|
||||
make_stock_entry(item_code, None, warehouse, 1, incoming_rate=10)
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||
|
||||
self.assertEqual([[1, 10]], eval(sle.stock_queue))
|
||||
|
||||
# negative qty
|
||||
make_zero(item_code, warehouse)
|
||||
make_stock_entry(item_code, warehouse, None, 1, incoming_rate=10)
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||
|
||||
self.assertEqual([[-1, 10]], eval(sle.stock_queue))
|
||||
|
||||
# further negative
|
||||
make_stock_entry(item_code, warehouse, None, 1)
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||
|
||||
self.assertEqual([[-2, 10]], eval(sle.stock_queue))
|
||||
|
||||
# move stock to positive
|
||||
make_stock_entry(item_code, None, warehouse, 3, incoming_rate=10)
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||
|
||||
self.assertEqual([[1, 10]], eval(sle.stock_queue))
|
||||
|
||||
frappe.db.set_default("allow_negative_stock", 0)
|
||||
|
||||
def test_auto_material_request(self):
|
||||
frappe.db.sql("""delete from `tabMaterial Request Item`""")
|
||||
frappe.db.sql("""delete from `tabMaterial Request`""")
|
||||
@@ -821,19 +871,19 @@ class TestStockEntry(unittest.TestCase):
|
||||
se = frappe.copy_doc(test_records[0]).insert()
|
||||
self.assertRaises (StockFreezeError, se.submit)
|
||||
frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0)
|
||||
|
||||
|
||||
def test_production_order(self):
|
||||
bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
|
||||
bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
|
||||
"is_default": 1, "docstatus": 1})
|
||||
|
||||
|
||||
production_order = frappe.new_doc("Production Order")
|
||||
production_order.update({
|
||||
"company": "_Test Company",
|
||||
"fg_warehouse": "_Test Warehouse 1 - _TC",
|
||||
"production_item": "_Test FG Item 2",
|
||||
"fg_warehouse": "_Test Warehouse 1 - _TC",
|
||||
"production_item": "_Test FG Item 2",
|
||||
"bom_no": bom_no,
|
||||
"qty": 1.0,
|
||||
"stock_uom": "Nos",
|
||||
"stock_uom": "Nos",
|
||||
"wip_warehouse": "_Test Warehouse - _TC"
|
||||
})
|
||||
production_order.insert()
|
||||
@@ -843,7 +893,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
|
||||
stock_entry = frappe.new_doc("Stock Entry")
|
||||
stock_entry.update({
|
||||
"purpose": "Manufacture/Repack",
|
||||
"purpose": "Manufacture",
|
||||
"production_order": production_order.name,
|
||||
"bom_no": bom_no,
|
||||
"fg_completed_qty": "1",
|
||||
|
||||
@@ -44,11 +44,14 @@ class StockLedgerEntry(Document):
|
||||
formatdate(self.posting_date), self.posting_time))
|
||||
|
||||
def validate_mandatory(self):
|
||||
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','actual_qty','company']
|
||||
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','company']
|
||||
for k in mandatory:
|
||||
if not self.get(k):
|
||||
frappe.throw(_("{0} is required").format(self.meta.get_label(k)))
|
||||
|
||||
if self.voucher_type != "Stock Reconciliation" and not self.actual_qty:
|
||||
frappe.throw(_("Actual Qty is mandatory"))
|
||||
|
||||
def validate_item(self):
|
||||
item_det = frappe.db.sql("""select name, has_batch_no, docstatus, is_stock_item
|
||||
from tabItem where name=%s""", self.item_code, as_dict=True)[0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"allow_copy": 1,
|
||||
"allow_copy": 1,
|
||||
"autoname": "SR/.######",
|
||||
"creation": "2013-03-28 10:35:31",
|
||||
"description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.",
|
||||
@@ -7,6 +7,7 @@
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"in_filter": 0,
|
||||
@@ -118,7 +119,7 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"max_attachments": 1,
|
||||
"modified": "2014-05-26 03:05:54.024413",
|
||||
"modified": "2014-10-07 12:43:52.825575",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Reconciliation",
|
||||
|
||||
@@ -16,13 +16,11 @@ class StockReconciliation(StockController):
|
||||
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
|
||||
|
||||
def validate(self):
|
||||
self.entries = []
|
||||
|
||||
self.validate_data()
|
||||
self.validate_expense_account()
|
||||
|
||||
def on_submit(self):
|
||||
self.insert_stock_ledger_entries()
|
||||
self.update_stock_ledger()
|
||||
self.make_gl_entries()
|
||||
|
||||
def on_cancel(self):
|
||||
@@ -126,10 +124,9 @@ class StockReconciliation(StockController):
|
||||
except Exception, e:
|
||||
self.validation_messages.append(_("Row # ") + ("%d: " % (row_num)) + cstr(e))
|
||||
|
||||
def insert_stock_ledger_entries(self):
|
||||
def update_stock_ledger(self):
|
||||
""" find difference between current and expected entries
|
||||
and create stock ledger entries based on the difference"""
|
||||
from erpnext.stock.utils import get_valuation_method
|
||||
from erpnext.stock.stock_ledger import get_previous_sle
|
||||
|
||||
row_template = ["item_code", "warehouse", "qty", "valuation_rate"]
|
||||
@@ -141,105 +138,27 @@ class StockReconciliation(StockController):
|
||||
for row_num, row in enumerate(data[data.index(self.head_row)+1:]):
|
||||
row = frappe._dict(zip(row_template, row))
|
||||
row["row_num"] = row_num
|
||||
previous_sle = get_previous_sle({
|
||||
"item_code": row.item_code,
|
||||
"warehouse": row.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time
|
||||
})
|
||||
|
||||
# check valuation rate mandatory
|
||||
if row.qty not in ["", None] and not row.valuation_rate and \
|
||||
flt(previous_sle.get("qty_after_transaction")) <= 0:
|
||||
frappe.throw(_("Valuation Rate required for Item {0}").format(row.item_code))
|
||||
if row.qty in ("", None) or row.valuation_rate in ("", None):
|
||||
previous_sle = get_previous_sle({
|
||||
"item_code": row.item_code,
|
||||
"warehouse": row.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time
|
||||
})
|
||||
|
||||
change_in_qty = row.qty not in ["", None] and \
|
||||
(flt(row.qty) - flt(previous_sle.get("qty_after_transaction")))
|
||||
if row.qty in ("", None):
|
||||
row.qty = previous_sle.get("qty_after_transaction")
|
||||
|
||||
change_in_rate = row.valuation_rate not in ["", None] and \
|
||||
(flt(row.valuation_rate) - flt(previous_sle.get("valuation_rate")))
|
||||
if row.valuation_rate in ("", None):
|
||||
row.valuation_rate = previous_sle.get("valuation_rate")
|
||||
|
||||
if get_valuation_method(row.item_code) == "Moving Average":
|
||||
self.sle_for_moving_avg(row, previous_sle, change_in_qty, change_in_rate)
|
||||
# if row.qty and not row.valuation_rate:
|
||||
# frappe.throw(_("Valuation Rate required for Item {0}").format(row.item_code))
|
||||
|
||||
else:
|
||||
self.sle_for_fifo(row, previous_sle, change_in_qty, change_in_rate)
|
||||
self.insert_entries(row)
|
||||
|
||||
def sle_for_moving_avg(self, row, previous_sle, change_in_qty, change_in_rate):
|
||||
"""Insert Stock Ledger Entries for Moving Average valuation"""
|
||||
def _get_incoming_rate(qty, valuation_rate, previous_qty, previous_valuation_rate):
|
||||
if previous_valuation_rate == 0:
|
||||
return flt(valuation_rate)
|
||||
else:
|
||||
if valuation_rate in ["", None]:
|
||||
valuation_rate = previous_valuation_rate
|
||||
return (qty * valuation_rate - previous_qty * previous_valuation_rate) \
|
||||
/ flt(qty - previous_qty)
|
||||
|
||||
if change_in_qty:
|
||||
# if change in qty, irrespective of change in rate
|
||||
incoming_rate = _get_incoming_rate(flt(row.qty), flt(row.valuation_rate),
|
||||
flt(previous_sle.get("qty_after_transaction")), flt(previous_sle.get("valuation_rate")))
|
||||
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry"
|
||||
self.insert_entries({"actual_qty": change_in_qty, "incoming_rate": incoming_rate}, row)
|
||||
|
||||
elif change_in_rate and flt(previous_sle.get("qty_after_transaction")) > 0:
|
||||
# if no change in qty, but change in rate
|
||||
# and positive actual stock before this reconciliation
|
||||
incoming_rate = _get_incoming_rate(
|
||||
flt(previous_sle.get("qty_after_transaction"))+1, flt(row.valuation_rate),
|
||||
flt(previous_sle.get("qty_after_transaction")),
|
||||
flt(previous_sle.get("valuation_rate")))
|
||||
|
||||
# +1 entry
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment +1"
|
||||
self.insert_entries({"actual_qty": 1, "incoming_rate": incoming_rate}, row)
|
||||
|
||||
# -1 entry
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment -1"
|
||||
self.insert_entries({"actual_qty": -1}, row)
|
||||
|
||||
def sle_for_fifo(self, row, previous_sle, change_in_qty, change_in_rate):
|
||||
"""Insert Stock Ledger Entries for FIFO valuation"""
|
||||
previous_stock_queue = json.loads(previous_sle.get("stock_queue") or "[]")
|
||||
previous_stock_qty = sum((batch[0] for batch in previous_stock_queue))
|
||||
previous_stock_value = sum((batch[0] * batch[1] for batch in \
|
||||
previous_stock_queue))
|
||||
|
||||
def _insert_entries():
|
||||
if previous_stock_queue != [[row.qty, row.valuation_rate]]:
|
||||
# make entry as per attachment
|
||||
if flt(row.qty):
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry"
|
||||
self.insert_entries({"actual_qty": row.qty,
|
||||
"incoming_rate": flt(row.valuation_rate)}, row)
|
||||
|
||||
# Make reverse entry
|
||||
if previous_stock_qty:
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Reverse Entry"
|
||||
self.insert_entries({"actual_qty": -1 * previous_stock_qty,
|
||||
"incoming_rate": previous_stock_qty < 0 and
|
||||
flt(row.valuation_rate) or 0}, row)
|
||||
|
||||
|
||||
if change_in_qty:
|
||||
if row.valuation_rate in ["", None]:
|
||||
# dont want change in valuation
|
||||
if previous_stock_qty > 0:
|
||||
# set valuation_rate as previous valuation_rate
|
||||
row.valuation_rate = previous_stock_value / flt(previous_stock_qty)
|
||||
|
||||
_insert_entries()
|
||||
|
||||
elif change_in_rate and previous_stock_qty > 0:
|
||||
# if no change in qty, but change in rate
|
||||
# and positive actual stock before this reconciliation
|
||||
|
||||
row.qty = previous_stock_qty
|
||||
_insert_entries()
|
||||
|
||||
def insert_entries(self, opts, row):
|
||||
def insert_entries(self, row):
|
||||
"""Insert Stock Ledger Entries"""
|
||||
args = frappe._dict({
|
||||
"doctype": "Stock Ledger Entry",
|
||||
@@ -251,16 +170,13 @@ class StockReconciliation(StockController):
|
||||
"voucher_no": self.name,
|
||||
"company": self.company,
|
||||
"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
|
||||
"voucher_detail_no": row.voucher_detail_no,
|
||||
"fiscal_year": self.fiscal_year,
|
||||
"is_cancelled": "No"
|
||||
"is_cancelled": "No",
|
||||
"qty_after_transaction": row.qty,
|
||||
"valuation_rate": row.valuation_rate
|
||||
})
|
||||
args.update(opts)
|
||||
self.make_sl_entries([args])
|
||||
|
||||
# append to entries
|
||||
self.entries.append(args)
|
||||
|
||||
def delete_and_repost_sle(self):
|
||||
""" Delete Stock Ledger Entries related to this voucher
|
||||
and repost future Stock Ledger Entries"""
|
||||
@@ -295,7 +211,7 @@ class StockReconciliation(StockController):
|
||||
|
||||
if not self.expense_account:
|
||||
msgprint(_("Please enter Expense Account"), raise_exception=1)
|
||||
elif not frappe.db.sql("""select * from `tabStock Ledger Entry`"""):
|
||||
elif not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""):
|
||||
if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
|
||||
frappe.throw(_("Difference Account must be a 'Liability' type account, since this Stock Reconciliation is an Opening Entry"))
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class TestStockReconciliation(unittest.TestCase):
|
||||
[20, "", "2012-12-26", "12:05", 16000, 15, 18000],
|
||||
[10, 2000, "2012-12-26", "12:10", 20000, 5, 6000],
|
||||
[1, 1000, "2012-12-01", "00:00", 1000, 11, 13200],
|
||||
[0, "", "2012-12-26", "12:10", 0, -5, 0]
|
||||
[0, "", "2012-12-26", "12:10", 0, -5, -6000]
|
||||
]
|
||||
|
||||
for d in input_data:
|
||||
@@ -63,16 +63,16 @@ class TestStockReconciliation(unittest.TestCase):
|
||||
input_data = [
|
||||
[50, 1000, "2012-12-26", "12:00", 50000, 45, 48000],
|
||||
[5, 1000, "2012-12-26", "12:00", 5000, 0, 0],
|
||||
[15, 1000, "2012-12-26", "12:00", 15000, 10, 12000],
|
||||
[15, 1000, "2012-12-26", "12:00", 15000, 10, 11500],
|
||||
[25, 900, "2012-12-26", "12:00", 22500, 20, 22500],
|
||||
[20, 500, "2012-12-26", "12:00", 10000, 15, 18000],
|
||||
[50, 1000, "2013-01-01", "12:00", 50000, 65, 68000],
|
||||
[5, 1000, "2013-01-01", "12:00", 5000, 20, 23000],
|
||||
["", 1000, "2012-12-26", "12:05", 15000, 10, 12000],
|
||||
["", 1000, "2012-12-26", "12:05", 15000, 10, 11500],
|
||||
[20, "", "2012-12-26", "12:05", 18000, 15, 18000],
|
||||
[10, 2000, "2012-12-26", "12:10", 20000, 5, 6000],
|
||||
[1, 1000, "2012-12-01", "00:00", 1000, 11, 13200],
|
||||
[0, "", "2012-12-26", "12:10", 0, -5, 0]
|
||||
[10, 2000, "2012-12-26", "12:10", 20000, 5, 7600],
|
||||
[1, 1000, "2012-12-01", "00:00", 1000, 11, 12512.73],
|
||||
[0, "", "2012-12-26", "12:10", 0, -5, -5142.86]
|
||||
|
||||
]
|
||||
|
||||
|
||||
@@ -6,18 +6,17 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
from frappe.utils import cint
|
||||
from frappe.model.document import Document
|
||||
|
||||
class StockSettings(Document):
|
||||
|
||||
def validate(self):
|
||||
for key in ["item_naming_by", "item_group", "stock_uom",
|
||||
"allow_negative_stock"]:
|
||||
for key in ["item_naming_by", "item_group", "stock_uom", "allow_negative_stock"]:
|
||||
frappe.db.set_default(key, self.get(key, ""))
|
||||
|
||||
|
||||
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
||||
set_by_naming_series("Item", "item_code",
|
||||
set_by_naming_series("Item", "item_code",
|
||||
self.get("item_naming_by")=="Naming Series", hide_name_field=True)
|
||||
|
||||
stock_frozen_limit = 356
|
||||
@@ -25,3 +24,5 @@ class StockSettings(Document):
|
||||
if submitted_stock_frozen > stock_frozen_limit:
|
||||
self.stock_frozen_upto_days = stock_frozen_limit
|
||||
frappe.msgprint (_("`Freeze Stocks Older Than` should be smaller than %d days.") %stock_frozen_limit)
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Average "age" of an Item in a particular Warehouse based on First-in-first-out (FIFO).
|
||||
@@ -1 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -1,183 +0,0 @@
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
frappe.pages['stock-ageing'].onload = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: __('Stock Ageing'),
|
||||
single_column: true
|
||||
});
|
||||
|
||||
new erpnext.StockAgeing(wrapper);
|
||||
|
||||
|
||||
wrapper.appframe.add_module_icon("Stock")
|
||||
|
||||
}
|
||||
|
||||
frappe.require("assets/erpnext/js/stock_grid_report.js");
|
||||
|
||||
erpnext.StockAgeing = erpnext.StockGridReport.extend({
|
||||
init: function(wrapper) {
|
||||
this._super({
|
||||
title: __("Stock Ageing"),
|
||||
page: wrapper,
|
||||
parent: $(wrapper).find('.layout-main'),
|
||||
appframe: wrapper.appframe,
|
||||
doctypes: ["Item", "Warehouse", "Stock Ledger Entry", "Item Group", "Brand", "Serial No"],
|
||||
})
|
||||
},
|
||||
setup_columns: function() {
|
||||
this.columns = [
|
||||
{id: "name", name: __("Item"), field: "name", width: 300,
|
||||
link_formatter: {
|
||||
open_btn: true,
|
||||
doctype: '"Item"'
|
||||
}},
|
||||
{id: "item_name", name: __("Item Name"), field: "item_name",
|
||||
width: 100, formatter: this.text_formatter},
|
||||
{id: "description", name: __("Description"), field: "description",
|
||||
width: 200, formatter: this.text_formatter},
|
||||
{id: "brand", name: __("Brand"), field: "brand", width: 100},
|
||||
{id: "average_age", name: __("Average Age"), field: "average_age",
|
||||
formatter: this.currency_formatter},
|
||||
{id: "earliest", name: __("Earliest"), field: "earliest",
|
||||
formatter: this.currency_formatter},
|
||||
{id: "latest", name: __("Latest"), field: "latest",
|
||||
formatter: this.currency_formatter},
|
||||
{id: "stock_uom", name: "UOM", field: "stock_uom", width: 100},
|
||||
];
|
||||
},
|
||||
filters: [
|
||||
{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse",
|
||||
default_value: "Select Warehouse..."},
|
||||
{fieldtype:"Select", label: __("Brand"), link:"Brand",
|
||||
default_value: "Select Brand...", filter: function(val, item, opts) {
|
||||
return val == opts.default_value || item.brand == val;
|
||||
}, link_formatter: {filter_input: "brand"}},
|
||||
{fieldtype:"Select", label: __("Plot By"),
|
||||
options: ["Average Age", "Earliest", "Latest"]},
|
||||
{fieldtype:"Date", label: __("To Date")},
|
||||
{fieldtype:"Button", label: __("Refresh"), icon:"icon-refresh icon-white"},
|
||||
{fieldtype:"Button", label: __("Reset Filters"), icon: "icon-filter"}
|
||||
],
|
||||
setup_filters: function() {
|
||||
var me = this;
|
||||
this._super();
|
||||
this.trigger_refresh_on_change(["warehouse", "plot_by", "brand"]);
|
||||
this.show_zero_check();
|
||||
},
|
||||
init_filter_values: function() {
|
||||
this._super();
|
||||
this.filter_inputs.to_date.val(dateutil.obj_to_user(new Date()));
|
||||
},
|
||||
prepare_data: function() {
|
||||
var me = this;
|
||||
|
||||
if(!this.data) {
|
||||
me._data = frappe.report_dump.data["Item"];
|
||||
me.item_by_name = me.make_name_map(me._data);
|
||||
}
|
||||
|
||||
this.data = [].concat(this._data);
|
||||
|
||||
this.serialized_buying_rates = this.get_serialized_buying_rates();
|
||||
|
||||
$.each(this.data, function(i, d) {
|
||||
me.reset_item_values(d);
|
||||
});
|
||||
|
||||
this.prepare_balances();
|
||||
|
||||
// filter out brand
|
||||
this.data = $.map(this.data, function(d) {
|
||||
return me.apply_filter(d, "brand") ? d : null;
|
||||
});
|
||||
|
||||
// filter out rows with zero values
|
||||
this.data = $.map(this.data, function(d) {
|
||||
return me.apply_zero_filter(null, d, null, me) ? d : null;
|
||||
});
|
||||
},
|
||||
prepare_balances: function() {
|
||||
var me = this;
|
||||
var to_date = dateutil.str_to_obj(this.to_date);
|
||||
var data = frappe.report_dump.data["Stock Ledger Entry"];
|
||||
|
||||
this.item_warehouse = {};
|
||||
|
||||
for(var i=0, j=data.length; i<j; i++) {
|
||||
var sl = data[i];
|
||||
var posting_date = dateutil.str_to_obj(sl.posting_date);
|
||||
|
||||
if(me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) {
|
||||
var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
|
||||
|
||||
// call diff to build fifo stack in item_warehouse
|
||||
var diff = me.get_value_diff(wh, sl, true);
|
||||
|
||||
if(posting_date > to_date)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$.each(me.data, function(i, item) {
|
||||
var full_fifo_stack = [];
|
||||
if(me.is_default("warehouse")) {
|
||||
$.each(me.item_warehouse[item.name] || {}, function(i, wh) {
|
||||
full_fifo_stack = full_fifo_stack.concat(wh.fifo_stack || [])
|
||||
});
|
||||
} else {
|
||||
full_fifo_stack = me.get_item_warehouse(me.warehouse, item.name).fifo_stack || [];
|
||||
}
|
||||
|
||||
var age_qty = total_qty = 0.0;
|
||||
var min_age = max_age = null;
|
||||
|
||||
$.each(full_fifo_stack, function(i, batch) {
|
||||
var batch_age = dateutil.get_diff(me.to_date, batch[2]);
|
||||
age_qty += batch_age * batch[0];
|
||||
total_qty += batch[0];
|
||||
max_age = Math.max(max_age, batch_age);
|
||||
if(min_age===null) min_age=batch_age;
|
||||
else min_age = Math.min(min_age, batch_age);
|
||||
});
|
||||
|
||||
item.average_age = total_qty.toFixed(2)==0.0 ? 0
|
||||
: (age_qty / total_qty).toFixed(2);
|
||||
item.earliest = max_age || 0.0;
|
||||
item.latest = min_age || 0.0;
|
||||
});
|
||||
|
||||
this.data = this.data.sort(function(a, b) {
|
||||
var sort_by = me.plot_by.replace(" ", "_").toLowerCase();
|
||||
return b[sort_by] - a[sort_by];
|
||||
});
|
||||
},
|
||||
get_plot_data: function() {
|
||||
var data = [];
|
||||
var me = this;
|
||||
|
||||
data.push({
|
||||
label: me.plot_by,
|
||||
data: $.map(me.data, function(item, idx) {
|
||||
return [[idx+1, item[me.plot_by.replace(" ", "_").toLowerCase() ]]]
|
||||
}),
|
||||
bars: {show: true},
|
||||
});
|
||||
|
||||
return data.length ? data : false;
|
||||
},
|
||||
get_plot_options: function() {
|
||||
var me = this;
|
||||
return {
|
||||
grid: { hoverable: true, clickable: true },
|
||||
xaxis: {
|
||||
ticks: $.map(me.data, function(item, idx) { return [[idx+1, item.name]] }),
|
||||
max: 15
|
||||
},
|
||||
series: { downsample: { threshold: 1000 } }
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"creation": "2012-09-21 20:15:14.000000",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"icon": "icon-table",
|
||||
"idx": 1,
|
||||
"modified": "2013-07-11 14:44:08.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "stock-ageing",
|
||||
"owner": "Administrator",
|
||||
"page_name": "stock-ageing",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Analytics"
|
||||
},
|
||||
{
|
||||
"role": "Material Manager"
|
||||
}
|
||||
],
|
||||
"standard": "Yes",
|
||||
"title": "Stock Ageing"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
Stock balances on a particular day, per warehouse.
|
||||
@@ -1,181 +0,0 @@
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.require("assets/erpnext/js/stock_analytics.js");
|
||||
|
||||
frappe.pages['stock-balance'].onload = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: __('Stock Balance'),
|
||||
single_column: true
|
||||
});
|
||||
|
||||
new erpnext.StockBalance(wrapper);
|
||||
|
||||
wrapper.appframe.add_module_icon("Stock");
|
||||
}
|
||||
|
||||
erpnext.StockBalance = erpnext.StockAnalytics.extend({
|
||||
init: function(wrapper) {
|
||||
this._super(wrapper, {
|
||||
title: __("Stock Balance"),
|
||||
doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand",
|
||||
"Stock Entry", "Project", "Serial No"],
|
||||
});
|
||||
},
|
||||
setup_columns: function() {
|
||||
this.columns = [
|
||||
{id: "name", name: __("Item"), field: "name", width: 300,
|
||||
formatter: this.tree_formatter},
|
||||
{id: "item_name", name: __("Item Name"), field: "item_name", width: 100},
|
||||
{id: "description", name: __("Description"), field: "description", width: 200,
|
||||
formatter: this.text_formatter},
|
||||
{id: "brand", name: __("Brand"), field: "brand", width: 100},
|
||||
{id: "stock_uom", name: __("UOM"), field: "stock_uom", width: 100},
|
||||
{id: "opening_qty", name: __("Opening Qty"), field: "opening_qty", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "inflow_qty", name: __("In Qty"), field: "inflow_qty", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "outflow_qty", name: __("Out Qty"), field: "outflow_qty", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "closing_qty", name: __("Closing Qty"), field: "closing_qty", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
|
||||
{id: "opening_value", name: __("Opening Value"), field: "opening_value", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "inflow_value", name: __("In Value"), field: "inflow_value", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "outflow_value", name: __("Out Value"), field: "outflow_value", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "closing_value", name: __("Closing Value"), field: "closing_value", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "valuation_rate", name: __("Valuation Rate"), field: "valuation_rate", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
];
|
||||
},
|
||||
|
||||
filters: [
|
||||
{fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand",
|
||||
default_value: __("Select Brand..."), filter: function(val, item, opts) {
|
||||
return val == opts.default_value || item.brand == val || item._show;
|
||||
}, link_formatter: {filter_input: "brand"}},
|
||||
{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse",
|
||||
default_value: __("Select Warehouse..."), filter: function(val, item, opts, me) {
|
||||
return me.apply_zero_filter(val, item, opts, me);
|
||||
}},
|
||||
{fieldtype:"Select", label: __("Project"), link:"Project", fieldname: "project",
|
||||
default_value: __("Select Project..."), filter: function(val, item, opts, me) {
|
||||
return me.apply_zero_filter(val, item, opts, me);
|
||||
}, link_formatter: {filter_input: "project"}},
|
||||
{fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
|
||||
{fieldtype:"Label", label: __("To")},
|
||||
{fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
|
||||
{fieldtype:"Button", label: __("Refresh"), icon:"icon-refresh icon-white"},
|
||||
{fieldtype:"Button", label: __("Reset Filters"), icon: "icon-filter"}
|
||||
],
|
||||
|
||||
setup_plot_check: function() {
|
||||
return;
|
||||
},
|
||||
|
||||
prepare_data: function() {
|
||||
this.stock_entry_map = this.make_name_map(frappe.report_dump.data["Stock Entry"], "name");
|
||||
this._super();
|
||||
},
|
||||
|
||||
prepare_balances: function() {
|
||||
var me = this;
|
||||
var from_date = dateutil.str_to_obj(this.from_date);
|
||||
var to_date = dateutil.str_to_obj(this.to_date);
|
||||
var data = frappe.report_dump.data["Stock Ledger Entry"];
|
||||
|
||||
this.item_warehouse = {};
|
||||
this.serialized_buying_rates = this.get_serialized_buying_rates();
|
||||
|
||||
for(var i=0, j=data.length; i<j; i++) {
|
||||
var sl = data[i];
|
||||
var sl_posting_date = dateutil.str_to_obj(sl.posting_date);
|
||||
|
||||
if((me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) &&
|
||||
(me.is_default("project") ? true : me.project == sl.project)) {
|
||||
var item = me.item_by_name[sl.item_code];
|
||||
var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
|
||||
var valuation_method = item.valuation_method ?
|
||||
item.valuation_method : sys_defaults.valuation_method;
|
||||
var is_fifo = valuation_method == "FIFO";
|
||||
|
||||
var qty_diff = sl.qty;
|
||||
var value_diff = me.get_value_diff(wh, sl, is_fifo);
|
||||
|
||||
if(sl_posting_date < from_date) {
|
||||
item.opening_qty += qty_diff;
|
||||
item.opening_value += value_diff;
|
||||
} else if(sl_posting_date <= to_date) {
|
||||
var ignore_inflow_outflow = this.is_default("warehouse")
|
||||
&& sl.voucher_type=="Stock Entry"
|
||||
&& this.stock_entry_map[sl.voucher_no].purpose=="Material Transfer";
|
||||
|
||||
if(!ignore_inflow_outflow) {
|
||||
if(qty_diff < 0) {
|
||||
item.outflow_qty += Math.abs(qty_diff);
|
||||
} else {
|
||||
item.inflow_qty += qty_diff;
|
||||
}
|
||||
if(value_diff < 0) {
|
||||
item.outflow_value += Math.abs(value_diff);
|
||||
} else {
|
||||
item.inflow_value += value_diff;
|
||||
}
|
||||
|
||||
item.closing_qty += qty_diff;
|
||||
item.closing_value += value_diff;
|
||||
}
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// opening + diff = closing
|
||||
// adding opening, since diff already added to closing
|
||||
$.each(me.item_by_name, function(key, item) {
|
||||
item.closing_qty += item.opening_qty;
|
||||
item.closing_value += item.opening_value;
|
||||
|
||||
// valuation rate
|
||||
if(!item.is_group && flt(item.closing_qty) > 0)
|
||||
item.valuation_rate = flt(item.closing_value) / flt(item.closing_qty);
|
||||
else item.valuation_rate = 0.0
|
||||
});
|
||||
},
|
||||
|
||||
update_groups: function() {
|
||||
var me = this;
|
||||
|
||||
$.each(this.data, function(i, item) {
|
||||
// update groups
|
||||
if(!item.is_group && me.apply_filter(item, "brand")) {
|
||||
var parent = me.parent_map[item.name];
|
||||
while(parent) {
|
||||
parent_group = me.item_by_name[parent];
|
||||
$.each(me.columns, function(c, col) {
|
||||
if (col.formatter == me.currency_formatter && col.field != "valuation_rate") {
|
||||
parent_group[col.field] = flt(parent_group[col.field]) + flt(item[col.field]);
|
||||
}
|
||||
});
|
||||
|
||||
// show parent if filtered by brand
|
||||
if(item.brand == me.brand)
|
||||
parent_group._show = true;
|
||||
|
||||
parent = me.parent_map[parent];
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
get_plot_data: function() {
|
||||
return;
|
||||
}
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"creation": "2012-12-27 18:57:47.000000",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"icon": "icon-table",
|
||||
"idx": 1,
|
||||
"modified": "2013-07-11 14:44:15.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "stock-balance",
|
||||
"owner": "Administrator",
|
||||
"page_name": "stock-balance",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Material Manager"
|
||||
},
|
||||
{
|
||||
"role": "Analytics"
|
||||
}
|
||||
],
|
||||
"standard": "Yes",
|
||||
"title": "Stock Balance"
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import flt, cint
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
@@ -57,6 +57,7 @@ def get_stock_ledger_entries(filters):
|
||||
conditions, as_dict=1)
|
||||
|
||||
def get_item_warehouse_batch_map(filters):
|
||||
float_precision = cint(frappe.db.get_default("float_precision")) or 3
|
||||
sle = get_stock_ledger_entries(filters)
|
||||
iwb_map = {}
|
||||
|
||||
@@ -67,14 +68,14 @@ def get_item_warehouse_batch_map(filters):
|
||||
}))
|
||||
qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no]
|
||||
if d.posting_date < filters["from_date"]:
|
||||
qty_dict.opening_qty += flt(d.actual_qty)
|
||||
qty_dict.opening_qty += flt(d.actual_qty, float_precision)
|
||||
elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]:
|
||||
if flt(d.actual_qty) > 0:
|
||||
qty_dict.in_qty += flt(d.actual_qty)
|
||||
qty_dict.in_qty += flt(d.actual_qty, float_precision)
|
||||
else:
|
||||
qty_dict.out_qty += abs(flt(d.actual_qty))
|
||||
qty_dict.out_qty += abs(flt(d.actual_qty, float_precision))
|
||||
|
||||
qty_dict.bal_qty += flt(d.actual_qty)
|
||||
qty_dict.bal_qty += flt(d.actual_qty, float_precision)
|
||||
|
||||
return iwb_map
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import date_diff
|
||||
from frappe.utils import date_diff, flt
|
||||
|
||||
def execute(filters=None):
|
||||
|
||||
|
||||
columns = get_columns()
|
||||
item_details = get_fifo_queue(filters)
|
||||
to_date = filters["to_date"]
|
||||
@@ -16,35 +16,39 @@ def execute(filters=None):
|
||||
fifo_queue = item_dict["fifo_queue"]
|
||||
details = item_dict["details"]
|
||||
if not fifo_queue: continue
|
||||
|
||||
|
||||
average_age = get_average_age(fifo_queue, to_date)
|
||||
earliest_age = date_diff(to_date, fifo_queue[0][1])
|
||||
latest_age = date_diff(to_date, fifo_queue[-1][1])
|
||||
|
||||
data.append([item, details.item_name, details.description, details.item_group,
|
||||
|
||||
data.append([item, details.item_name, details.description, details.item_group,
|
||||
details.brand, average_age, earliest_age, latest_age, details.stock_uom])
|
||||
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_average_age(fifo_queue, to_date):
|
||||
batch_age = age_qty = total_qty = 0.0
|
||||
for batch in fifo_queue:
|
||||
batch_age = date_diff(to_date, batch[1])
|
||||
age_qty += batch_age * batch[0]
|
||||
total_qty += batch[0]
|
||||
|
||||
|
||||
return (age_qty / total_qty) if total_qty else 0.0
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [_("Item Code") + ":Link/Item:100", _("Item Name") + "::100", _("Description") + "::200",
|
||||
_("Item Group") + ":Link/Item Group:100", _("Brand") + ":Link/Brand:100", _("Average Age") + ":Float:100",
|
||||
return [_("Item Code") + ":Link/Item:100", _("Item Name") + "::100", _("Description") + "::200",
|
||||
_("Item Group") + ":Link/Item Group:100", _("Brand") + ":Link/Brand:100", _("Average Age") + ":Float:100",
|
||||
_("Earliest") + ":Int:80", _("Latest") + ":Int:80", _("UOM") + ":Link/UOM:100"]
|
||||
|
||||
|
||||
def get_fifo_queue(filters):
|
||||
item_details = {}
|
||||
for d in get_stock_ledger_entries(filters):
|
||||
item_details.setdefault(d.name, {"details": d, "fifo_queue": []})
|
||||
fifo_queue = item_details[d.name]["fifo_queue"]
|
||||
|
||||
if d.voucher_type == "Stock Reconciliation":
|
||||
d.actual_qty = flt(d.qty_after_transaction) - flt(item_details[d.name].get("qty_after_transaction", 0))
|
||||
|
||||
if d.actual_qty > 0:
|
||||
fifo_queue.append([d.actual_qty, d.posting_date])
|
||||
else:
|
||||
@@ -52,7 +56,7 @@ def get_fifo_queue(filters):
|
||||
while qty_to_pop:
|
||||
batch = fifo_queue[0] if fifo_queue else [0, None]
|
||||
if 0 < batch[0] <= qty_to_pop:
|
||||
# if batch qty > 0
|
||||
# if batch qty > 0
|
||||
# not enough or exactly same qty in current batch, clear batch
|
||||
qty_to_pop -= batch[0]
|
||||
fifo_queue.pop(0)
|
||||
@@ -61,12 +65,14 @@ def get_fifo_queue(filters):
|
||||
batch[0] -= qty_to_pop
|
||||
qty_to_pop = 0
|
||||
|
||||
item_details[d.name]["qty_after_transaction"] = d.qty_after_transaction
|
||||
|
||||
return item_details
|
||||
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
return frappe.db.sql("""select
|
||||
item.name, item.item_name, item_group, brand, description, item.stock_uom,
|
||||
actual_qty, posting_date
|
||||
return frappe.db.sql("""select
|
||||
item.name, item.item_name, item_group, brand, description, item.stock_uom,
|
||||
actual_qty, posting_date, voucher_type, qty_after_transaction
|
||||
from `tabStock Ledger Entry` sle,
|
||||
(select name, item_name, description, stock_uom, brand, item_group
|
||||
from `tabItem` {item_conditions}) item
|
||||
@@ -77,19 +83,19 @@ def get_stock_ledger_entries(filters):
|
||||
order by posting_date, posting_time, sle.name"""\
|
||||
.format(item_conditions=get_item_conditions(filters),
|
||||
sle_conditions=get_sle_conditions(filters)), filters, as_dict=True)
|
||||
|
||||
|
||||
def get_item_conditions(filters):
|
||||
conditions = []
|
||||
if filters.get("item_code"):
|
||||
conditions.append("item_code=%(item_code)s")
|
||||
if filters.get("brand"):
|
||||
conditions.append("brand=%(brand)s")
|
||||
|
||||
|
||||
return "where {}".format(" and ".join(conditions)) if conditions else ""
|
||||
|
||||
|
||||
def get_sle_conditions(filters):
|
||||
conditions = []
|
||||
if filters.get("warehouse"):
|
||||
conditions.append("warehouse=%(warehouse)s")
|
||||
|
||||
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
||||
|
||||
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Warehouse-Wise Stock Balance"] = {
|
||||
frappe.query_reports["Stock Balance"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
@@ -18,4 +18,4 @@ frappe.query_reports["Warehouse-Wise Stock Balance"] = {
|
||||
"default": frappe.datetime.get_today()
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-06-05 11:00:31",
|
||||
"creation": "2014-10-10 17:58:11.577901",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2014-06-03 07:18:17.384923",
|
||||
"modified": "2014-10-10 17:58:11.577901",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Warehouse-Wise Stock Balance",
|
||||
"name": "Stock Balance",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Stock Ledger Entry",
|
||||
"report_name": "Warehouse-Wise Stock Balance",
|
||||
"report_name": "Stock Balance",
|
||||
"report_type": "Script Report"
|
||||
}
|
||||
@@ -58,10 +58,10 @@ def get_conditions(filters):
|
||||
#get all details
|
||||
def get_stock_ledger_entries(filters):
|
||||
conditions = get_conditions(filters)
|
||||
return frappe.db.sql("""select item_code, warehouse, posting_date,
|
||||
actual_qty, valuation_rate, stock_uom, company
|
||||
return frappe.db.sql("""select item_code, warehouse, posting_date, actual_qty, valuation_rate,
|
||||
stock_uom, company, voucher_type, qty_after_transaction, stock_value_difference
|
||||
from `tabStock Ledger Entry`
|
||||
where docstatus < 2 %s order by item_code, warehouse""" %
|
||||
where docstatus < 2 %s order by posting_date, posting_time, name""" %
|
||||
conditions, as_dict=1)
|
||||
|
||||
def get_item_warehouse_map(filters):
|
||||
@@ -71,30 +71,36 @@ def get_item_warehouse_map(filters):
|
||||
for d in sle:
|
||||
iwb_map.setdefault(d.company, {}).setdefault(d.item_code, {}).\
|
||||
setdefault(d.warehouse, frappe._dict({\
|
||||
"opening_qty": 0.0, "opening_val": 0.0,
|
||||
"in_qty": 0.0, "in_val": 0.0,
|
||||
"out_qty": 0.0, "out_val": 0.0,
|
||||
"bal_qty": 0.0, "bal_val": 0.0,
|
||||
"opening_qty": 0.0, "opening_val": 0.0,
|
||||
"in_qty": 0.0, "in_val": 0.0,
|
||||
"out_qty": 0.0, "out_val": 0.0,
|
||||
"bal_qty": 0.0, "bal_val": 0.0,
|
||||
"val_rate": 0.0, "uom": None
|
||||
}))
|
||||
qty_dict = iwb_map[d.company][d.item_code][d.warehouse]
|
||||
qty_dict.uom = d.stock_uom
|
||||
|
||||
if d.voucher_type == "Stock Reconciliation":
|
||||
qty_diff = flt(d.qty_after_transaction) - qty_dict.bal_qty
|
||||
else:
|
||||
qty_diff = flt(d.actual_qty)
|
||||
|
||||
value_diff = flt(d.stock_value_difference)
|
||||
|
||||
if d.posting_date < filters["from_date"]:
|
||||
qty_dict.opening_qty += flt(d.actual_qty)
|
||||
qty_dict.opening_val += flt(d.actual_qty * d.valuation_rate)
|
||||
qty_dict.opening_qty += qty_diff
|
||||
qty_dict.opening_val += value_diff
|
||||
elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]:
|
||||
qty_dict.val_rate = d.valuation_rate
|
||||
|
||||
if flt(d.actual_qty) > 0:
|
||||
qty_dict.in_qty += flt(d.actual_qty)
|
||||
qty_dict.in_val += flt(d.actual_qty * d.valuation_rate)
|
||||
if qty_diff > 0:
|
||||
qty_dict.in_qty += qty_diff
|
||||
qty_dict.in_val += value_diff
|
||||
else:
|
||||
qty_dict.out_qty += abs(flt(d.actual_qty))
|
||||
qty_dict.out_val += flt(abs(flt(d.actual_qty)) * d.valuation_rate)
|
||||
qty_dict.out_qty += abs(qty_diff)
|
||||
qty_dict.out_val += abs(value_diff)
|
||||
|
||||
qty_dict.bal_qty += flt(d.actual_qty)
|
||||
qty_dict.bal_val += flt(d.actual_qty * d.valuation_rate)
|
||||
qty_dict.bal_qty += qty_diff
|
||||
qty_dict.bal_val += value_diff
|
||||
|
||||
return iwb_map
|
||||
|
||||
@@ -13,16 +13,13 @@ def execute(filters=None):
|
||||
data = []
|
||||
for sle in sl_entries:
|
||||
item_detail = item_details[sle.item_code]
|
||||
voucher_link_icon = """<a href="%s"><i class="icon icon-share"
|
||||
style="cursor: pointer;"></i></a>""" \
|
||||
% ("/".join(["#Form", sle.voucher_type, sle.voucher_no]),)
|
||||
|
||||
data.append([sle.date, sle.item_code, item_detail.item_name, item_detail.item_group,
|
||||
item_detail.brand, item_detail.description, sle.warehouse,
|
||||
item_detail.stock_uom, sle.actual_qty, sle.qty_after_transaction,
|
||||
(sle.incoming_rate if sle.actual_qty > 0 else 0.0),
|
||||
sle.valuation_rate, sle.stock_value, sle.voucher_type, sle.voucher_no,
|
||||
voucher_link_icon, sle.batch_no, sle.serial_no, sle.company])
|
||||
sle.batch_no, sle.serial_no, sle.company])
|
||||
|
||||
return columns, data
|
||||
|
||||
@@ -31,7 +28,7 @@ def get_columns():
|
||||
_("Brand") + ":Link/Brand:100", _("Description") + "::200", _("Warehouse") + ":Link/Warehouse:100",
|
||||
_("Stock UOM") + ":Link/UOM:100", _("Qty") + ":Float:50", _("Balance Qty") + ":Float:100",
|
||||
_("Incoming Rate") + ":Currency:110", _("Valuation Rate") + ":Currency:110", _("Balance Value") + ":Currency:110",
|
||||
_("Voucher Type") + "::110", _("Voucher #") + "::100", _("Link") + "::30", _("Batch") + ":Link/Batch:100",
|
||||
_("Voucher Type") + "::110", _("Voucher #") + ":Dynamic Link/Voucher Type:100", _("Batch") + ":Link/Batch:100",
|
||||
_("Serial #") + ":Link/Serial No:100", _("Company") + ":Link/Company:100"]
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
|
||||
@@ -27,7 +27,7 @@ def make_sl_entries(sl_entries, is_amended=None):
|
||||
if sle.get('is_cancelled') == 'Yes':
|
||||
sle['actual_qty'] = -flt(sle['actual_qty'])
|
||||
|
||||
if sle.get("actual_qty"):
|
||||
if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation":
|
||||
sle_id = make_entry(sle)
|
||||
|
||||
args = sle.copy()
|
||||
@@ -36,9 +36,9 @@ def make_sl_entries(sl_entries, is_amended=None):
|
||||
"is_amended": is_amended
|
||||
})
|
||||
update_bin(args)
|
||||
|
||||
if cancel:
|
||||
delete_cancelled_entry(sl_entries[0].get('voucher_type'),
|
||||
sl_entries[0].get('voucher_no'))
|
||||
delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
|
||||
|
||||
def set_as_cancel(voucher_type, voucher_no):
|
||||
frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes',
|
||||
@@ -58,7 +58,7 @@ def delete_cancelled_entry(voucher_type, voucher_no):
|
||||
frappe.db.sql("""delete from `tabStock Ledger Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
def update_entries_after(args, verbose=1):
|
||||
def update_entries_after(args, allow_zero_rate=False, verbose=1):
|
||||
"""
|
||||
update valution rate and qty after transaction
|
||||
from the current time-bucket onwards
|
||||
@@ -83,7 +83,6 @@ def update_entries_after(args, verbose=1):
|
||||
|
||||
entries_to_fix = get_sle_after_datetime(previous_sle or \
|
||||
{"item_code": args["item_code"], "warehouse": args["warehouse"]}, for_update=True)
|
||||
|
||||
valuation_method = get_valuation_method(args["item_code"])
|
||||
stock_value_difference = 0.0
|
||||
|
||||
@@ -95,21 +94,30 @@ def update_entries_after(args, verbose=1):
|
||||
qty_after_transaction += flt(sle.actual_qty)
|
||||
continue
|
||||
|
||||
|
||||
if sle.serial_no:
|
||||
valuation_rate = get_serialized_values(qty_after_transaction, sle, valuation_rate)
|
||||
elif valuation_method == "Moving Average":
|
||||
valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate)
|
||||
else:
|
||||
valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue)
|
||||
qty_after_transaction += flt(sle.actual_qty)
|
||||
|
||||
qty_after_transaction += flt(sle.actual_qty)
|
||||
else:
|
||||
if sle.voucher_type=="Stock Reconciliation":
|
||||
valuation_rate = sle.valuation_rate
|
||||
qty_after_transaction = sle.qty_after_transaction
|
||||
stock_queue = [[qty_after_transaction, valuation_rate]]
|
||||
else:
|
||||
if valuation_method == "Moving Average":
|
||||
valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate, allow_zero_rate)
|
||||
else:
|
||||
valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue, allow_zero_rate)
|
||||
|
||||
|
||||
qty_after_transaction += flt(sle.actual_qty)
|
||||
|
||||
# get stock value
|
||||
if sle.serial_no:
|
||||
stock_value = qty_after_transaction * valuation_rate
|
||||
elif valuation_method == "Moving Average":
|
||||
stock_value = (qty_after_transaction > 0) and \
|
||||
(qty_after_transaction * valuation_rate) or 0
|
||||
stock_value = qty_after_transaction * valuation_rate
|
||||
else:
|
||||
stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue))
|
||||
|
||||
@@ -243,69 +251,72 @@ def get_serialized_values(qty_after_transaction, sle, valuation_rate):
|
||||
|
||||
return valuation_rate
|
||||
|
||||
def get_moving_average_values(qty_after_transaction, sle, valuation_rate):
|
||||
def get_moving_average_values(qty_after_transaction, sle, valuation_rate, allow_zero_rate):
|
||||
incoming_rate = flt(sle.incoming_rate)
|
||||
actual_qty = flt(sle.actual_qty)
|
||||
|
||||
if not incoming_rate:
|
||||
# In case of delivery/stock issue in_rate = 0 or wrong incoming rate
|
||||
incoming_rate = valuation_rate
|
||||
if flt(sle.actual_qty) > 0:
|
||||
if qty_after_transaction < 0 and not valuation_rate:
|
||||
# if negative stock, take current valuation rate as incoming rate
|
||||
valuation_rate = incoming_rate
|
||||
|
||||
elif qty_after_transaction < 0:
|
||||
# if negative stock, take current valuation rate as incoming rate
|
||||
valuation_rate = incoming_rate
|
||||
new_stock_qty = abs(qty_after_transaction) + actual_qty
|
||||
new_stock_value = (abs(qty_after_transaction) * valuation_rate) + (actual_qty * incoming_rate)
|
||||
|
||||
new_stock_qty = qty_after_transaction + actual_qty
|
||||
new_stock_value = qty_after_transaction * valuation_rate + actual_qty * incoming_rate
|
||||
if new_stock_qty:
|
||||
valuation_rate = new_stock_value / flt(new_stock_qty)
|
||||
elif not valuation_rate and qty_after_transaction <= 0:
|
||||
valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, allow_zero_rate)
|
||||
|
||||
if new_stock_qty > 0 and new_stock_value > 0:
|
||||
valuation_rate = new_stock_value / flt(new_stock_qty)
|
||||
elif new_stock_qty <= 0:
|
||||
valuation_rate = 0.0
|
||||
return abs(flt(valuation_rate))
|
||||
|
||||
# NOTE: val_rate is same as previous entry if new stock value is negative
|
||||
|
||||
return valuation_rate
|
||||
|
||||
def get_fifo_values(qty_after_transaction, sle, stock_queue):
|
||||
def get_fifo_values(qty_after_transaction, sle, stock_queue, allow_zero_rate):
|
||||
incoming_rate = flt(sle.incoming_rate)
|
||||
actual_qty = flt(sle.actual_qty)
|
||||
if not stock_queue:
|
||||
stock_queue.append([0, 0])
|
||||
|
||||
if actual_qty > 0:
|
||||
if not stock_queue:
|
||||
stock_queue.append([0, 0])
|
||||
|
||||
if stock_queue[-1][0] > 0:
|
||||
stock_queue.append([actual_qty, incoming_rate])
|
||||
else:
|
||||
qty = stock_queue[-1][0] + actual_qty
|
||||
stock_queue[-1] = [qty, qty > 0 and incoming_rate or 0]
|
||||
if qty == 0:
|
||||
stock_queue.pop(-1)
|
||||
else:
|
||||
stock_queue[-1] = [qty, incoming_rate]
|
||||
else:
|
||||
incoming_cost = 0
|
||||
qty_to_pop = abs(actual_qty)
|
||||
while qty_to_pop:
|
||||
if not stock_queue:
|
||||
stock_queue.append([0, 0])
|
||||
stock_queue.append([0, get_valuation_rate(sle.item_code, sle.warehouse, allow_zero_rate)
|
||||
if qty_after_transaction <= 0 else 0])
|
||||
|
||||
batch = stock_queue[0]
|
||||
|
||||
if 0 < batch[0] <= qty_to_pop:
|
||||
# if batch qty > 0
|
||||
# not enough or exactly same qty in current batch, clear batch
|
||||
incoming_cost += flt(batch[0]) * flt(batch[1])
|
||||
qty_to_pop -= batch[0]
|
||||
if qty_to_pop >= batch[0]:
|
||||
# consume current batch
|
||||
qty_to_pop = qty_to_pop - batch[0]
|
||||
stock_queue.pop(0)
|
||||
if not stock_queue and qty_to_pop:
|
||||
# stock finished, qty still remains to be withdrawn
|
||||
# negative stock, keep in as a negative batch
|
||||
stock_queue.append([-qty_to_pop, batch[1]])
|
||||
break
|
||||
|
||||
else:
|
||||
# all from current batch
|
||||
incoming_cost += flt(qty_to_pop) * flt(batch[1])
|
||||
batch[0] -= qty_to_pop
|
||||
# qty found in current batch
|
||||
# consume it and exit
|
||||
batch[0] = batch[0] - qty_to_pop
|
||||
qty_to_pop = 0
|
||||
|
||||
stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue))
|
||||
stock_qty = sum((flt(batch[0]) for batch in stock_queue))
|
||||
|
||||
valuation_rate = stock_qty and (stock_value / flt(stock_qty)) or 0
|
||||
valuation_rate = (stock_value / flt(stock_qty)) if stock_qty else 0
|
||||
|
||||
return valuation_rate
|
||||
return abs(valuation_rate)
|
||||
|
||||
def _raise_exceptions(args, verbose=1):
|
||||
deficiency = min(e["diff"] for e in _exceptions)
|
||||
@@ -337,3 +348,26 @@ def get_previous_sle(args, for_update=False):
|
||||
"timestamp(posting_date, posting_time) <= timestamp(%(posting_date)s, %(posting_time)s)"],
|
||||
"desc", "limit 1", for_update=for_update)
|
||||
return sle and sle[0] or {}
|
||||
|
||||
def get_valuation_rate(item_code, warehouse, allow_zero_rate=False):
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
from `tabStock Ledger Entry`
|
||||
where item_code = %s and warehouse = %s
|
||||
and ifnull(valuation_rate, 0) > 0
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse))
|
||||
|
||||
if not last_valuation_rate:
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
from `tabStock Ledger Entry`
|
||||
where item_code = %s and ifnull(valuation_rate, 0) > 0
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", item_code)
|
||||
|
||||
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
|
||||
|
||||
if not valuation_rate:
|
||||
valuation_rate = frappe.db.get_value("Item Price", {"item_code": item_code, "buying": 1}, "price_list_rate")
|
||||
|
||||
if not allow_zero_rate and not valuation_rate and cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")):
|
||||
frappe.throw(_("Purchase rate for item: {0} not found, which is required to book accounting entry (expense). Please mention item price against a buying price list.").format(item_code))
|
||||
|
||||
return valuation_rate
|
||||
|
||||
@@ -5,7 +5,6 @@ import frappe
|
||||
from frappe import _
|
||||
import json
|
||||
from frappe.utils import flt, cstr, nowdate, add_days, cint
|
||||
from frappe.defaults import get_global_default
|
||||
from frappe.utils.email_lib import sendmail
|
||||
from erpnext.accounts.utils import get_fiscal_year, FiscalYearError
|
||||
|
||||
@@ -94,14 +93,14 @@ def get_valuation_method(item_code):
|
||||
"""get valuation method from item or default"""
|
||||
val_method = frappe.db.get_value('Item', item_code, 'valuation_method')
|
||||
if not val_method:
|
||||
val_method = get_global_default('valuation_method') or "FIFO"
|
||||
val_method = frappe.db.get_value("Stock Settings", None, "valuation_method") or "FIFO"
|
||||
return val_method
|
||||
|
||||
def get_fifo_rate(previous_stock_queue, qty):
|
||||
"""get FIFO (average) Rate from Queue"""
|
||||
if qty >= 0:
|
||||
total = sum(f[0] for f in previous_stock_queue)
|
||||
return total and sum(f[0] * f[1] for f in previous_stock_queue) / flt(total) or 0.0
|
||||
return sum(flt(f[0]) * flt(f[1]) for f in previous_stock_queue) / flt(total) if total else 0.0
|
||||
else:
|
||||
available_qty_for_outgoing, outgoing_cost = 0, 0
|
||||
qty_to_pop = abs(qty)
|
||||
|
||||
@@ -51,9 +51,14 @@ cur_frm.fields_dict['serial_no'].get_query = function(doc, cdt, cdn) {
|
||||
['Serial No', 'docstatus', '!=', 2],
|
||||
['Serial No', 'status', '=', "Delivered"]
|
||||
];
|
||||
if(doc.item_code) cond = ['Serial No', 'item_code', '=', doc.item_code];
|
||||
if(doc.customer) cond = ['Serial No', 'customer', '=', doc.customer];
|
||||
filter.push(cond);
|
||||
if(doc.item_code) {
|
||||
cond = ['Serial No', 'item_code', '=', doc.item_code];
|
||||
filter.push(cond);
|
||||
}
|
||||
if(doc.customer) {
|
||||
cond = ['Serial No', 'customer', '=', doc.customer];
|
||||
filter.push(cond);
|
||||
}
|
||||
return{
|
||||
filters:filter
|
||||
}
|
||||
|
||||
@@ -34,20 +34,20 @@
|
||||
"<a href=""#Sales Browser/Territory"">Add / Edit</a>","<a href=""#Sales Browser/Territory""> إضافة / تحرير < / A>"
|
||||
"<h4>Default Template</h4><p>Uses <a href=""http://jinja.pocoo.org/docs/templates/"">Jinja Templating</a> and all the fields of Address (including Custom Fields if any) will be available</p><pre><code>{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}{{ city }}<br>{% if state %}{{ state }}<br>{% endif -%}{% if pincode %} PIN: {{ pincode }}<br>{% endif -%}{{ country }}<br>{% if phone %}Phone: {{ phone }}<br>{% endif -%}{% if fax %}Fax: {{ fax }}<br>{% endif -%}{% if email_id %}Email: {{ email_id }}<br>{% endif -%}</code></pre>","<h4> افتراضي قالب </ H4> <p> ويستخدم <a href=""http://jinja.pocoo.org/docs/templates/""> جنجا القولبة </ a> و كافة الحقول من العنوان ( بما في ذلك الحقول المخصصة إن وجدت) وسوف تكون متاحة </ P> <PRE> على <code> {{}} address_line1 <BR> {٪ إذا address_line2٪} {{}} address_line2 <BR> { ENDIF٪ -٪} {{المدينة}} <BR> {٪ إذا الدولة٪} {{الدولة}} {<BR>٪ ENDIF -٪} {٪ إذا كان الرقم السري٪} PIN: {{}} الرقم السري {<BR>٪ ENDIF -٪} {{البلد}} <BR> {٪ إذا كان الهاتف٪} الهاتف: {{هاتف}} {<BR> ENDIF٪ -٪} {٪ إذا الفاكس٪} فاكس: {{}} الفاكس <BR> {٪ ENDIF -٪} {٪٪ إذا email_id} البريد الإلكتروني: {{}} email_id <BR> ؛ {٪ ENDIF -٪} </ رمز> </ قبل>"
|
||||
A Customer Group exists with same name please change the Customer name or rename the Customer Group,يوجد مجموعة العملاء مع نفس الاسم الرجاء تغيير اسم العميل أو إعادة تسمية المجموعة العملاء
|
||||
A Customer exists with same name,العملاء من وجود نفس الاسم مع
|
||||
A Customer exists with same name,يوجد في قائمة العملاء عميل بنفس الاسم
|
||||
A Lead with this email id should exist,وينبغي أن يكون هذا المعرف الرصاص مع البريد الإلكتروني موجود
|
||||
A Product or Service,منتج أو خدمة
|
||||
A Supplier exists with same name,وهناك مورد موجود مع نفس الاسم
|
||||
A symbol for this currency. For e.g. $,رمزا لهذه العملة. على سبيل المثال ل$
|
||||
AMC Expiry Date,AMC تاريخ انتهاء الاشتراك
|
||||
Abbr,ابر
|
||||
Abbreviation cannot have more than 5 characters,اختصار لا يمكن أن يكون أكثر من 5 أحرف
|
||||
Abbreviation cannot have more than 5 characters,الاختصار لا يمكن أن يكون أكثر من 5 أحرف
|
||||
Above Value,فوق القيمة
|
||||
Absent,غائب
|
||||
Acceptance Criteria,معايير القبول
|
||||
Accepted,مقبول
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},يجب أن يكون مقبول مرفوض + الكمية مساوية ل كمية تلقى القطعة ل {0}
|
||||
Accepted Quantity,قبلت الكمية
|
||||
Accepted Quantity,كمية مقبولة
|
||||
Accepted Warehouse,قبلت مستودع
|
||||
Account,حساب
|
||||
Account Balance,رصيد حسابك
|
||||
@@ -65,12 +65,12 @@ Account with child nodes cannot be converted to ledger,حساب مع العقد
|
||||
Account with existing transaction can not be converted to group.,حساب مع الصفقة الحالية لا يمكن تحويلها إلى المجموعة.
|
||||
Account with existing transaction can not be deleted,حساب مع الصفقة الحالية لا يمكن حذف
|
||||
Account with existing transaction cannot be converted to ledger,حساب مع الصفقة الحالية لا يمكن تحويلها إلى دفتر الأستاذ
|
||||
Account {0} cannot be a Group,حساب {0} لا يمكن أن تكون المجموعة
|
||||
Account {0} does not belong to Company {1},حساب {0} لا تنتمي إلى شركة {1}
|
||||
Account {0} cannot be a Group,حساب {0} لا يمكن أن يكون مجموعة
|
||||
Account {0} does not belong to Company {1},حساب {0} لا ينتمي إلى شركة {1}
|
||||
Account {0} does not belong to company: {1},حساب {0} لا تنتمي إلى الشركة: {1}
|
||||
Account {0} does not exist,حساب {0} غير موجود
|
||||
Account {0} has been entered more than once for fiscal year {1},حساب {0} تم إدخال أكثر من مرة للعام المالي {1}
|
||||
Account {0} is frozen,حساب {0} يتم تجميد
|
||||
Account {0} has been entered more than once for fiscal year {1},تم إدخال حساب {0} أكثر من مرة للعام المالي {1}
|
||||
Account {0} is frozen,حساب {0} مجمد
|
||||
Account {0} is inactive,حساب {0} غير نشط
|
||||
Account {0} is not valid,حساب {0} غير صالح
|
||||
Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item,"حساب {0} يجب أن تكون من النوع ' الأصول الثابتة ""كما البند {1} هو البند الأصول"
|
||||
@@ -85,8 +85,8 @@ Accounting,المحاسبة
|
||||
"Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",قيد محاسبي المجمدة تصل إلى هذا التاريخ، لا أحد يمكن أن تفعل / تعديل إدخال باستثناء دور المحددة أدناه.
|
||||
Accounting journal entries.,المحاسبة إدخالات دفتر اليومية.
|
||||
Accounts,حسابات
|
||||
Accounts Browser,حسابات متصفح
|
||||
Accounts Frozen Upto,حسابات مجمدة لغاية
|
||||
Accounts Browser,متصفح الحسابات
|
||||
Accounts Frozen Upto,حسابات مجمدة حتي
|
||||
Accounts Payable,ذمم دائنة
|
||||
Accounts Receivable,حسابات القبض
|
||||
Accounts Settings,إعدادات الحسابات
|
||||
@@ -94,14 +94,14 @@ Active,نشط
|
||||
Active: Will extract emails from ,نشط: سيتم استخراج رسائل البريد الإلكتروني من
|
||||
Activity,نشاط
|
||||
Activity Log,سجل النشاط
|
||||
Activity Log:,النشاط المفتاح:
|
||||
Activity Type,النشاط نوع
|
||||
Activity Log:,:سجل النشاط
|
||||
Activity Type,نوع النشاط
|
||||
Actual,فعلي
|
||||
Actual Budget,الميزانية الفعلية
|
||||
Actual Completion Date,تاريخ الإنتهاء الفعلي
|
||||
Actual Date,تاريخ الفعلية
|
||||
Actual Date,التاريخ الفعلي
|
||||
Actual End Date,تاريخ الإنتهاء الفعلي
|
||||
Actual Invoice Date,الفعلي تاريخ الفاتورة
|
||||
Actual Invoice Date,التاريخ الفعلي للفاتورة
|
||||
Actual Posting Date,تاريخ النشر الفعلي
|
||||
Actual Qty,الكمية الفعلية
|
||||
Actual Qty (at source/target),الكمية الفعلية (في المصدر / الهدف)
|
||||
@@ -112,7 +112,7 @@ Actual Start Date,تاريخ البدء الفعلي
|
||||
Add,إضافة
|
||||
Add / Edit Taxes and Charges,إضافة / تعديل الضرائب والرسوم
|
||||
Add Child,إضافة الطفل
|
||||
Add Serial No,إضافة رقم المسلسل
|
||||
Add Serial No,إضافة رقم تسلسلي
|
||||
Add Taxes,إضافة الضرائب
|
||||
Add Taxes and Charges,إضافة الضرائب والرسوم
|
||||
Add or Deduct,إضافة أو خصم
|
||||
@@ -131,7 +131,7 @@ Address Line 2,العنوان سطر 2
|
||||
Address Template,قالب عنوان
|
||||
Address Title,عنوان عنوان
|
||||
Address Title is mandatory.,عنوان عنوانها إلزامية.
|
||||
Address Type,عنوان نوع
|
||||
Address Type,نوع العنوان
|
||||
Address master.,عنوان رئيسي.
|
||||
Administrative Expenses,المصاريف الإدارية
|
||||
Administrative Officer,موظف إداري
|
||||
@@ -217,7 +217,7 @@ Amount to Bill,تصل إلى بيل
|
||||
An Customer exists with same name,موجود على العملاء مع نفس الاسم
|
||||
"An Item Group exists with same name, please change the item name or rename the item group",وجود فريق المدينة مع نفس الاسم، الرجاء تغيير اسم العنصر أو إعادة تسمية المجموعة البند
|
||||
"An item exists with same name ({0}), please change the item group name or rename the item",عنصر موجود مع نفس الاسم ( {0} ) ، الرجاء تغيير اسم المجموعة البند أو إعادة تسمية هذا البند
|
||||
Analyst,المحلل
|
||||
Analyst,محلل
|
||||
Annual,سنوي
|
||||
Another Period Closing Entry {0} has been made after {1},دخول أخرى الفترة الإنتهاء {0} أحرز بعد {1}
|
||||
Another Salary Structure {0} is active for employee {0}. Please make its status 'Inactive' to proceed.,"هيكل الرواتب أخرى {0} نشطة للموظف {0} . يرجى التأكد مكانتها ""غير نشطة "" والمضي قدما."
|
||||
@@ -266,15 +266,15 @@ Atleast one of the Selling or Buying must be selected,يجب تحديد الاق
|
||||
Atleast one warehouse is mandatory,واحدة على الاقل مستودع إلزامي
|
||||
Attach Image,إرفاق صورة
|
||||
Attach Letterhead,نعلق رأسية
|
||||
Attach Logo,نعلق شعار
|
||||
Attach Your Picture,نعلق صورتك
|
||||
Attach Logo,إرفاق صورة الشعار/العلامة التجارية
|
||||
Attach Your Picture,إرفاق صورتك
|
||||
Attendance,الحضور
|
||||
Attendance Date,تاريخ الحضور
|
||||
Attendance Details,تفاصيل الحضور
|
||||
Attendance From Date,الحضور من تاريخ
|
||||
Attendance From Date and Attendance To Date is mandatory,الحضور من التسجيل والحضور إلى تاريخ إلزامي
|
||||
Attendance To Date,الحضور إلى تاريخ
|
||||
Attendance can not be marked for future dates,لا يمكن أن تكون علامة لحضور تواريخ مستقبلية
|
||||
Attendance can not be marked for future dates,لا يمكن أن ىكون تاريخ الحضور تاريخ مستقبلي
|
||||
Attendance for employee {0} is already marked,الحضور للموظف {0} تم وضع علامة بالفعل
|
||||
Attendance record.,سجل الحضور.
|
||||
Authorization Control,إذن التحكم
|
||||
@@ -287,13 +287,13 @@ Automatically extract Job Applicants from a mail box ,
|
||||
Automatically extract Leads from a mail box e.g.,استخراج الشراء تلقائيا من صندوق البريد على سبيل المثال
|
||||
Automatically updated via Stock Entry of type Manufacture/Repack,تحديثها تلقائيا عن طريق إدخال الأسهم الصنع نوع / أعد حزم
|
||||
Automotive,السيارات
|
||||
Autoreply when a new mail is received,عندما رد تلقائي تلقي بريد جديد
|
||||
Autoreply when a new mail is received,رد تلقائي عند تلقي بريد جديد
|
||||
Available,متاح
|
||||
Available Qty at Warehouse,الكمية المتاحة في مستودع
|
||||
Available Stock for Packing Items,الأسهم المتاحة للتعبئة وحدات
|
||||
"Available in BOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet",المتاحة في BOM ، تسليم مذكرة ، شراء الفاتورة ، ترتيب الإنتاج، طلب شراء ، شراء استلام ، فاتورة المبيعات ، ترتيب المبيعات ، اسهم الدخول و الجدول الزمني
|
||||
Average Age,متوسط العمر
|
||||
Average Commission Rate,متوسط سعر جنة
|
||||
Average Commission Rate,متوسط العمولة
|
||||
Average Discount,متوسط الخصم
|
||||
Awesome Products,المنتجات رهيبة
|
||||
Awesome Services,خدمات رهيبة
|
||||
@@ -331,9 +331,9 @@ Bank Clearance Summary,بنك ملخص التخليص
|
||||
Bank Draft,البنك مشروع
|
||||
Bank Name,اسم البنك
|
||||
Bank Overdraft Account,حساب السحب على المكشوف المصرفي
|
||||
Bank Reconciliation,البنك المصالحة
|
||||
Bank Reconciliation Detail,البنك المصالحة تفاصيل
|
||||
Bank Reconciliation Statement,بيان التسويات المصرفية
|
||||
Bank Reconciliation,تسوية البنك
|
||||
Bank Reconciliation Detail,تفاصيل تسوية البنك
|
||||
Bank Reconciliation Statement,بيان تسوية البنك
|
||||
Bank Voucher,البنك قسيمة
|
||||
Bank/Cash Balance,بنك / النقد وما في حكمه
|
||||
Banking,مصرفي
|
||||
@@ -405,21 +405,21 @@ Bundle items at time of sale.,حزمة البنود في وقت البيع.
|
||||
Business Development Manager,مدير تطوير الأعمال
|
||||
Buying,شراء
|
||||
Buying & Selling,شراء وبيع
|
||||
Buying Amount,شراء المبلغ
|
||||
Buying Settings,شراء إعدادات
|
||||
Buying Amount,مبلغ الشراء
|
||||
Buying Settings,إعدادات الشراء
|
||||
"Buying must be checked, if Applicable For is selected as {0}",يجب أن يتم التحقق الشراء، إذا تم تحديد مطبق للك {0}
|
||||
C-Form,نموذج C-
|
||||
C-Form Applicable,C-نموذج قابل للتطبيق
|
||||
C-Form Invoice Detail,C-نموذج تفاصيل الفاتورة
|
||||
C-Form No,C-الاستمارة رقم
|
||||
C-Form records,سجلات نموذج C-
|
||||
C-Form Invoice Detail, تفاصيل الفاتورة نموذج - س
|
||||
C-Form No,رقم النموذج - س
|
||||
C-Form records,سجلات النموذج - س
|
||||
CENVAT Capital Goods,CENVAT السلع الرأسمالية
|
||||
CENVAT Edu Cess,CENVAT ايدو سيس
|
||||
CENVAT SHE Cess,CENVAT SHE سيس
|
||||
CENVAT Service Tax,CENVAT ضريبة الخدمة
|
||||
CENVAT Service Tax Cess 1,خدمة CENVAT ضريبة سيس 1
|
||||
CENVAT Service Tax Cess 2,خدمة CENVAT ضريبة سيس 2
|
||||
Calculate Based On,حساب الربح بناء على
|
||||
Calculate Based On,إحسب الربح بناء على
|
||||
Calculate Total Score,حساب النتيجة الإجمالية
|
||||
Calendar Events,الأحداث
|
||||
Call,دعوة
|
||||
@@ -538,7 +538,7 @@ Commission on Sales,عمولة على المبيعات
|
||||
Commission rate cannot be greater than 100,معدل العمولة لا يمكن أن يكون أكبر من 100
|
||||
Communication,اتصالات
|
||||
Communication HTML,الاتصالات HTML
|
||||
Communication History,الاتصال التاريخ
|
||||
Communication History,تاريخ الاتصال
|
||||
Communication log.,سجل الاتصالات.
|
||||
Communications,الاتصالات
|
||||
Company,شركة
|
||||
@@ -1025,7 +1025,7 @@ Extract Emails,استخراج رسائل البريد الإلكتروني
|
||||
FCFS Rate,FCFS قيم
|
||||
Failed: ,فشل:
|
||||
Family Background,الخلفية العائلية
|
||||
Fax,بالفاكس
|
||||
Fax,فاكس
|
||||
Features Setup,ميزات الإعداد
|
||||
Feed,أطعم
|
||||
Feed Type,إطعام نوع
|
||||
@@ -1041,7 +1041,7 @@ Financial / accounting year.,المالية / المحاسبة العام.
|
||||
Financial Analytics,تحليلات مالية
|
||||
Financial Services,الخدمات المالية
|
||||
Financial Year End Date,تاريخ نهاية السنة المالية
|
||||
Financial Year Start Date,السنة المالية تاريخ بدء
|
||||
Financial Year Start Date,تاريخ بدء السنة المالية
|
||||
Finished Goods,السلع تامة الصنع
|
||||
First Name,الاسم الأول
|
||||
First Responded On,أجاب أولا على
|
||||
@@ -1059,7 +1059,7 @@ Food,غذاء
|
||||
For Company,لشركة
|
||||
For Employee,لموظف
|
||||
For Employee Name,لاسم الموظف
|
||||
For Price List,ل ائحة الأسعار
|
||||
For Price List,لائحة الأسعار
|
||||
For Production,للإنتاج
|
||||
For Reference Only.,للإشارة فقط.
|
||||
For Sales Invoice,لفاتورة المبيعات
|
||||
@@ -1823,7 +1823,7 @@ Notify by Email on creation of automatic Material Request,إبلاغ عن طري
|
||||
Number Format,عدد تنسيق
|
||||
Offer Date,عرض التسجيل
|
||||
Office,مكتب
|
||||
Office Equipments,معدات المكاتب
|
||||
Office Equipments,أدوات المكتب
|
||||
Office Maintenance Expenses,مصاريف صيانة المكاتب
|
||||
Office Rent,مكتب للإيجار
|
||||
Old Parent,العمر الرئيسي
|
||||
@@ -1840,7 +1840,7 @@ Open Production Orders,أوامر مفتوحة الانتاج
|
||||
Open Tickets,تذاكر مفتوحة
|
||||
Opening (Cr),افتتاح (الكروم )
|
||||
Opening (Dr),افتتاح ( الدكتور )
|
||||
Opening Date,فتح تاريخ
|
||||
Opening Date,تاريخ الفتح
|
||||
Opening Entry,فتح دخول
|
||||
Opening Qty,فتح الكمية
|
||||
Opening Time,يفتح من الساعة
|
||||
@@ -1854,7 +1854,7 @@ Operation {0} is repeated in Operations Table,عملية {0} يتكرر في ج
|
||||
Operation {0} not present in Operations Table,عملية {0} غير موجودة في جدول العمليات
|
||||
Operations,عمليات
|
||||
Opportunity,فرصة
|
||||
Opportunity Date,الفرصة تاريخ
|
||||
Opportunity Date,تاريخ الفرصة
|
||||
Opportunity From,فرصة من
|
||||
Opportunity Item,فرصة السلعة
|
||||
Opportunity Items,فرصة الأصناف
|
||||
@@ -1914,9 +1914,9 @@ Packing Slip,زلة التعبئة
|
||||
Packing Slip Item,التعبئة الإغلاق زلة
|
||||
Packing Slip Items,التعبئة عناصر زلة
|
||||
Packing Slip(s) cancelled,زلة التعبئة (ق ) إلغاء
|
||||
Page Break,الصفحة استراحة
|
||||
Page Name,الصفحة اسم
|
||||
Paid Amount,دفع المبلغ
|
||||
Page Break,فاصل الصفحة
|
||||
Page Name,اسم الصفحة
|
||||
Paid Amount,المبلغ المدفوع
|
||||
Paid amount + Write Off Amount can not be greater than Grand Total,المبلغ المدفوع + شطب المبلغ لا يمكن أن يكون أكبر من المجموع الكلي
|
||||
Pair,زوج
|
||||
Parameter,المعلمة
|
||||
@@ -3128,7 +3128,7 @@ Users with this role are allowed to create / modify accounting entry before froz
|
||||
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,يسمح للمستخدمين مع هذا الدور لضبط الحسابات المجمدة و إنشاء / تعديل القيود المحاسبية على حسابات مجمدة
|
||||
Utilities,خدمات
|
||||
Utility Expenses,مصاريف فائدة
|
||||
Valid For Territories,صالحة للالأقاليم
|
||||
Valid For Territories,صالحة للأقاليم
|
||||
Valid From,صالحة من
|
||||
Valid Upto,صالحة لغاية
|
||||
Valid for Territories,صالحة للالأقاليم
|
||||
@@ -3237,7 +3237,7 @@ Write Off Voucher,شطب قسيمة
|
||||
Wrong Template: Unable to find head row.,قالب الخطأ: تعذر العثور على صف الرأس.
|
||||
Year,عام
|
||||
Year Closed,مغلق العام
|
||||
Year End Date,نهاية التاريخ العام
|
||||
Year End Date,تاريخ نهاية العام
|
||||
Year Name,اسم العام
|
||||
Year Start Date,تاريخ بدء العام
|
||||
Year of Passing,اجتياز سنة
|
||||
@@ -3261,8 +3261,8 @@ You have entered duplicate items. Please rectify and try again.,لقد دخلت
|
||||
You may need to update: {0},قد تحتاج إلى تحديث : {0}
|
||||
You must Save the form before proceeding,يجب حفظ النموذج قبل الشروع
|
||||
Your Customer's TAX registration numbers (if applicable) or any general information,عميلك أرقام التسجيل الضريبي (إن وجدت) أو أي معلومات عامة
|
||||
Your Customers,الزبائن
|
||||
Your Login Id,تسجيل الدخول اسم المستخدم الخاص بك
|
||||
Your Customers,العملاء
|
||||
Your Login Id,تسجيل الدخول - اسم المستخدم الخاص بك
|
||||
Your Products or Services,المنتجات أو الخدمات الخاصة بك
|
||||
Your Suppliers,لديك موردون
|
||||
Your email address,عنوان البريد الإلكتروني الخاص بك
|
||||
|
||||
|
@@ -40,14 +40,14 @@
|
||||
"<a href=""#Sales Browser/Item Group"">Add / Edit</a>","<a href=""#Sales Browser/Item Group""> Hinzufügen / Bearbeiten </ a>"
|
||||
"<a href=""#Sales Browser/Territory"">Add / Edit</a>","<a href=""#Sales Browser/Territory""> Hinzufügen / Bearbeiten </ a>"
|
||||
"<h4>Default Template</h4><p>Uses <a href=""http://jinja.pocoo.org/docs/templates/"">Jinja Templating</a> and all the fields of Address (including Custom Fields if any) will be available</p><pre><code>{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}{{ city }}<br>{% if state %}{{ state }}<br>{% endif -%}{% if pincode %} PIN: {{ pincode }}<br>{% endif -%}{{ country }}<br>{% if phone %}Phone: {{ phone }}<br>{% endif -%}{% if fax %}Fax: {{ fax }}<br>{% endif -%}{% if email_id %}Email: {{ email_id }}<br>{% endif -%}</code></pre>",
|
||||
A Customer Group exists with same name please change the Customer name or rename the Customer Group,Eine Kundengruppe mit dem gleichen Namen existiert bereits. Ändern Sie den Kundennamen oder nennen Sie die Kundengruppe um
|
||||
A Customer Group exists with same name please change the Customer name or rename the Customer Group,Eine Kundengruppe mit dem gleichen Namen existiert bereits. Ändern Sie den Kundennamen oder benennen Sie die Kundengruppe um
|
||||
A Customer exists with same name,Ein Kunde mit dem gleichen Namen existiert bereits
|
||||
A Lead with this email id should exist,Ein Lead mit dieser E-Mail Adresse sollte vorhanden sein
|
||||
A Product or Service,Ein Produkt oder Dienstleistung
|
||||
"A Product or a Service that is bought, sold or kept in stock.","Ein Produkt oder eine Dienstleistung, die gekauft, verkauft oder auf Lager gehalten wird."
|
||||
A Lead with this email id should exist,Ein Leiter mit dieser E-Mail-Adresse sollte vorhanden sein
|
||||
A Product or Service,Produkt oder Dienstleistung
|
||||
"A Product or a Service that is bought, sold or kept in stock.","Produkt oder Dienstleistung, die gekauft, verkauft oder auf Lager gehalten wird."
|
||||
A Supplier exists with same name,Ein Lieferant mit dem gleichen Namen existiert bereits
|
||||
A condition for a Shipping Rule,Eine Bedingung für eine Versandregel
|
||||
A logical Warehouse against which stock entries are made.,"Eine logisches Warenlager, für das Bestandseinträge gemacht werden."
|
||||
A condition for a Shipping Rule,Bedingung für eine Versandregel
|
||||
A logical Warehouse against which stock entries are made.,Eine logisches Warenlager für das Bestandseinträge gemacht werden.
|
||||
A symbol for this currency. For e.g. $,"Ein Symbol für diese Währung, z.B. €"
|
||||
A third party distributor / dealer / commission agent / affiliate / reseller who sells the companies products for a commission.,"Ein dritter Vertriebspartner/Händler/Kommissionär/Tochterunternehmer/ Fachhändler, der die Produkte es Unternehmens auf Provision vertreibt."
|
||||
"A user with ""Expense Approver"" role",
|
||||
@@ -65,13 +65,13 @@ Account,Konto
|
||||
Account Balance,Kontostand
|
||||
Account Created: {0},Konto {0} erstellt
|
||||
Account Details,Kontendaten
|
||||
Account Head,Konto Kopf
|
||||
Account Name,Konto Name
|
||||
Account Type,Konto Typ
|
||||
Account Head,Kontoführer
|
||||
Account Name,Kontoname
|
||||
Account Type,Kontotyp
|
||||
"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'","Kontostand bereits im Kredit, Sie dürfen nicht eingestellt ""Balance Must Be"" als ""Debit"""
|
||||
"Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'","Kontostand bereits in Lastschrift, sind Sie nicht berechtigt, festgelegt als ""Kredit"" ""Balance Must Be '"
|
||||
Account for the warehouse (Perpetual Inventory) will be created under this Account.,Konto für das Lager (Laufende Inventur) wird unter diesem Konto erstellt.
|
||||
Account head {0} created,Konto Kopf {0} erstellt
|
||||
Account head {0} created,Kontoführer {0} erstellt
|
||||
Account must be a balance sheet account,Konto muss ein Bilanzkonto sein
|
||||
Account with child nodes cannot be converted to ledger,Ein Konto mit Kind-Knoten kann nicht in ein Kontoblatt umgewandelt werden
|
||||
Account with existing transaction can not be converted to group.,Ein Konto mit bestehenden Transaktion kann nicht in eine Gruppe umgewandelt werden
|
||||
@@ -79,11 +79,11 @@ Account with existing transaction can not be deleted,Ein Konto mit bestehenden T
|
||||
Account with existing transaction cannot be converted to ledger,Ein Konto mit bestehenden Transaktion kann nicht in ein Kontoblatt umgewandelt werden
|
||||
Account {0} cannot be a Group,Konto {0} kann keine Gruppe sein
|
||||
Account {0} does not belong to Company {1},Konto {0} gehört nicht zum Unternehmen {1}
|
||||
Account {0} does not belong to company: {1},Konto {0} gehört nicht zum Unternehmen {1}
|
||||
Account {0} does not belong to company: {1},Konto {0} gehört nicht zum Unternehmen: {1}
|
||||
Account {0} does not exist,Konto {0} existiert nicht
|
||||
Account {0} does not exists,
|
||||
Account {0} has been entered more than once for fiscal year {1},Konto {0} wurde mehr als einmal für das Geschäftsjahr {1} eingegeben
|
||||
Account {0} is frozen,Konto {0} ist eingefroren
|
||||
Account {0} is frozen,Konto {0} ist gesperrt
|
||||
Account {0} is inactive,Konto {0} ist inaktiv
|
||||
Account {0} is not valid,Konto {0} ist nicht gültig
|
||||
Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item,"Konto {0} muss vom Typ ""Aktivposten"" sein, weil der Artikel {1} ein Aktivposten ist"
|
||||
@@ -99,18 +99,18 @@ Accounting Entry for Stock,
|
||||
"Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.","Bis zu diesem Zeitpunkt eingefrorener Buchhaltungseintrag, niemand außer der unten genannten Position kann den Eintrag bearbeiten/ändern."
|
||||
Accounting journal entries.,Buchhaltungsjournaleinträge
|
||||
Accounts,Konten
|
||||
Accounts Browser,Konten Browser
|
||||
Accounts Frozen Upto,Eingefrorene Konten bis
|
||||
Accounts Manager,
|
||||
Accounts Browser,Kontenbrowser
|
||||
Accounts Frozen Upto,Konten gesperrt bis
|
||||
Accounts Manager,Kontenmanager
|
||||
Accounts Payable,Kreditoren
|
||||
Accounts Receivable,Forderungen
|
||||
Accounts Settings,Kontoeinstellungen
|
||||
Accounts User,
|
||||
Accounts Settings,Konteneinstellungen
|
||||
Accounts User,Kontenbenutzer
|
||||
Active,Aktiv
|
||||
Active: Will extract emails from ,Aktiv: Werden E-Mails extrahieren
|
||||
Activity,Aktivität
|
||||
Activity Log,Aktivitätsprotokoll
|
||||
Activity Log:,Activity Log:
|
||||
Activity Log:,Aktivitätsprotokoll:
|
||||
Activity Type,Aktivitätstyp
|
||||
Actual,Tatsächlich
|
||||
Actual Budget,Tatsächliches Budget
|
||||
@@ -127,7 +127,7 @@ Actual Quantity,Istmenge
|
||||
Actual Start Date,Tatsächliches Startdatum
|
||||
Add,Hinzufügen
|
||||
Add / Edit Taxes and Charges,Hinzufügen/Bearbeiten von Steuern und Abgaben
|
||||
Add Child,Kind hinzufügen
|
||||
Add Child,Untergeordnetes Element hinzufügen
|
||||
Add Serial No,In Seriennummer
|
||||
Add Taxes,Steuern hinzufügen
|
||||
Add or Deduct,Hinzuaddieren oder abziehen
|
||||
@@ -338,12 +338,12 @@ BOM {0} is not submitted or inactive BOM for Item {1},Stückliste {0} nicht vorg
|
||||
Backup Manager,Datensicherungsverwaltung
|
||||
Backup Right Now,Jetzt eine Datensicherung durchführen
|
||||
Backups will be uploaded to,Datensicherungen werden hochgeladen nach
|
||||
Balance Qty,Bilanz Menge
|
||||
Balance Qty,Bilanzmenge
|
||||
Balance Sheet,Bilanz
|
||||
Balance Value,Bilanzwert
|
||||
Balance for Account {0} must always be {1},Saldo für Konto {0} muss immer {1} sein
|
||||
Balance must be,Saldo muss sein
|
||||
"Balances of Accounts of type ""Bank"" or ""Cash""","Guthaben von Konten vom Typ "" Bank"" oder "" Cash"""
|
||||
"Balances of Accounts of type ""Bank"" or ""Cash""","Guthaben von Konten vom Typ ""Bank"" oder ""Cash"""
|
||||
Bank,Bank
|
||||
Bank / Cash Account,Bank / Geldkonto
|
||||
Bank A/C No.,Bankkonto-Nr.
|
||||
@@ -351,25 +351,25 @@ Bank Account,Bankkonto
|
||||
Bank Account No.,Bankkonto-Nr.
|
||||
Bank Accounts,Bankkonten
|
||||
Bank Clearance Summary,Zusammenfassung Bankgenehmigung
|
||||
Bank Draft,Bank Draft
|
||||
Bank Draft,Banktratte
|
||||
Bank Name,Name der Bank
|
||||
Bank Overdraft Account,Kontokorrentkredit Konto
|
||||
Bank Reconciliation,Kontenabstimmung
|
||||
Bank Reconciliation Detail,Kontenabstimmungsdetail
|
||||
Bank Reconciliation Statement,Kontenabstimmungsauszug
|
||||
Bank Voucher,Bankgutschein
|
||||
Bank Voucher,Bankbeleg
|
||||
Bank/Cash Balance,Bank-/Bargeldsaldo
|
||||
Banking,Bankwesen
|
||||
Barcode,Strichcode
|
||||
Barcode {0} already used in Item {1},Barcode {0} bereits im Artikel verwendet {1}
|
||||
Barcode {0} already used in Item {1},Barcode {0} bereits in Artikel {1} verwendet
|
||||
Based On,Beruht auf
|
||||
Basic,Grundlagen
|
||||
Basic Info,Basis Informationen
|
||||
Basic Information,Basis Informationen
|
||||
Basic Rate,Basisrate
|
||||
Basic Rate (Company Currency),Basisrate (Unternehmenswährung)
|
||||
Basic Info,Grundinfo
|
||||
Basic Information,Grundinformationen
|
||||
Basic Rate,Grundrate
|
||||
Basic Rate (Company Currency),Grundrate (Unternehmenswährung)
|
||||
Batch,Stapel
|
||||
Batch (lot) of an Item.,Stapel (Charge) eines Artikels.
|
||||
Batch (lot) of an Item.,Stapel (Partie) eines Artikels.
|
||||
Batch Finished Date,Enddatum des Stapels
|
||||
Batch ID,Stapel-ID
|
||||
Batch No,Stapelnr.
|
||||
@@ -381,7 +381,7 @@ Batched for Billing,Für Abrechnung gebündelt
|
||||
Better Prospects,Bessere zukünftige Kunden
|
||||
Bill Date,Rechnungsdatum
|
||||
Bill No,Rechnungsnr.
|
||||
Bill of Material,Bill of Material
|
||||
Bill of Material,Stückliste
|
||||
Bill of Material to be considered for manufacturing,"Stückliste, die für die Herstellung berücksichtigt werden soll"
|
||||
Bill of Materials (BOM),Stückliste (SL)
|
||||
Billable,Abrechenbar
|
||||
@@ -389,7 +389,7 @@ Billed,Abgerechnet
|
||||
Billed Amount,Rechnungsbetrag
|
||||
Billed Amt,Rechnungsbetrag
|
||||
Billing,Abrechnung
|
||||
Billing (Sales Invoice),
|
||||
Billing (Sales Invoice),Abrechung (Handelsrechnung)
|
||||
Billing Address,Rechnungsadresse
|
||||
Billing Address Name,Name der Rechnungsadresse
|
||||
Billing Status,Abrechnungsstatus
|
||||
|
||||
|
@@ -41,8 +41,8 @@ A Product or Service,Ένα Προϊόν ή Υπηρεσία
|
||||
A Supplier exists with same name,Ένας προμηθευτής υπάρχει με το ίδιο όνομα
|
||||
A symbol for this currency. For e.g. $,Ένα σύμβολο για το νόμισμα αυτό. Για παράδειγμα $
|
||||
AMC Expiry Date,AMC Ημερομηνία Λήξης
|
||||
Abbr,Abbr
|
||||
Abbreviation cannot have more than 5 characters,Συντομογραφία δεν μπορεί να έχει περισσότερα από 5 χαρακτήρες
|
||||
Abbr,Συντ.
|
||||
Abbreviation cannot have more than 5 characters,Μια συντομογραφία δεν μπορεί να έχει περισσότερα από 5 χαρακτήρες
|
||||
Above Value,Πάνω Value
|
||||
Absent,Απών
|
||||
Acceptance Criteria,Κριτήρια αποδοχής
|
||||
@@ -50,9 +50,9 @@ Accepted,Δεκτός
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},Αποδεκτές + Απορρίπτεται Ποσότητα πρέπει να είναι ίση με Ελήφθη ποσότητα για τη θέση {0}
|
||||
Accepted Quantity,ΠΟΣΟΤΗΤΑ
|
||||
Accepted Warehouse,Αποδεκτές αποθήκη
|
||||
Account,λογαριασμός
|
||||
Account Balance,Υπόλοιπο λογαριασμού
|
||||
Account Created: {0},Ο λογαριασμός Δημιουργήθηκε : {0}
|
||||
Account,Λογαριασμός
|
||||
Account Balance,Υπόλοιπο Λογαριασμού
|
||||
Account Created: {0},Ο λογαριασμός δημιουργήθηκε : {0}
|
||||
Account Details,Στοιχεία Λογαριασμού
|
||||
Account Head,Επικεφαλής λογαριασμού
|
||||
Account Name,Όνομα λογαριασμού
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -35,41 +35,42 @@
|
||||
"<a href=""#Sales Browser/Territory"">Add / Edit</a>","<a href=""#Sales Browser/Territory""> Ajouter / Modifier < / a>"
|
||||
"<h4>Default Template</h4><p>Uses <a href=""http://jinja.pocoo.org/docs/templates/"">Jinja Templating</a> and all the fields of Address (including Custom Fields if any) will be available</p><pre><code>{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}{{ city }}<br>{% if state %}{{ state }}<br>{% endif -%}{% if pincode %} PIN: {{ pincode }}<br>{% endif -%}{{ country }}<br>{% if phone %}Phone: {{ phone }}<br>{% endif -%}{% if fax %}Fax: {{ fax }}<br>{% endif -%}{% if email_id %}Email: {{ email_id }}<br>{% endif -%}</code></pre>","<h4> modèle par défaut </ h4> <p> Utilise <a href=""http://jinja.pocoo.org/docs/templates/""> Jinja création de modèles </ a> et tous les domaines de l'Adresse ( y compris les champs personnalisés cas échéant) sera disponible </ p> <pre> <code> {{}} address_line1 Photos {% si address_line2%} {{}} address_line2 <br> { % endif -%} {{ville}} Photos {% si l'état%} {{état}} {% endif Photos -%} {% if%} code PIN PIN: {{code PIN}} {% endif Photos -%} {{pays}} Photos {% si le téléphone%} Téléphone: {{phone}} {<br> % endif -%} {% if%} fax Fax: {{fax}} {% endif Photos -%} {% if%} email_id Email: {{}} email_id Photos ; {% endif -%} </ code> </ pre>"
|
||||
A Customer Group exists with same name please change the Customer name or rename the Customer Group,BOM récursivité : {0} ne peut pas être le parent ou l'enfant de {2}
|
||||
A Customer exists with same name,Une clientèle existe avec le même nom
|
||||
A Lead with this email id should exist,Un responsable de cette id e-mail doit exister
|
||||
A Product or Service,Nouveau N ° de série ne peut pas avoir d'entrepôt . Entrepôt doit être réglé par Stock entrée ou ticket de caisse
|
||||
A Supplier exists with same name,Un Fournisseur existe avec le même nom
|
||||
A symbol for this currency. For e.g. $,Un symbole de cette monnaie. Pour exemple $
|
||||
A Customer exists with same name,Un client existe avec le même nom
|
||||
A Lead with this email id should exist,Un responsable de cet identifiant de courriel doit exister
|
||||
A Product or Service,Un produit ou service
|
||||
A Supplier exists with same name,Un fournisseur existe avec ce même nom
|
||||
A symbol for this currency. For e.g. $,Un symbole pour cette monnaie. Par exemple $
|
||||
AMC Expiry Date,AMC Date d'expiration
|
||||
Abbr,Abbr
|
||||
Abbreviation cannot have more than 5 characters,Compte avec la transaction existante ne peut pas être converti en groupe.
|
||||
Abbreviation cannot have more than 5 characters,L'abbréviation ne peut pas avoir plus de 5 caractères
|
||||
Above Value,Au-dessus de la valeur
|
||||
Absent,Absent
|
||||
Acceptance Criteria,Critères d'acceptation
|
||||
Accepted,Accepté
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},Compte {0} doit être SAMES comme débit pour tenir compte de la facture de vente en ligne {0}
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},"La quantité acceptée + rejetée doit être égale à la quantité reçue pour l'Item {0}
|
||||
Compte {0} doit être SAMES comme débit pour tenir compte de la facture de vente en ligne {0}"
|
||||
Accepted Quantity,Quantité acceptés
|
||||
Accepted Warehouse,Entrepôt acceptés
|
||||
Accepted Warehouse,Entrepôt acceptable
|
||||
Account,compte
|
||||
Account Balance,Solde du compte
|
||||
Account Created: {0},Compte créé : {0}
|
||||
Account Details,Détails du compte
|
||||
Account Head,Chef du compte
|
||||
Account Head,Responsable du compte
|
||||
Account Name,Nom du compte
|
||||
Account Type,Type de compte
|
||||
"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'","Le solde du compte déjà en crédit, vous n'êtes pas autorisé à mettre en 'équilibre doit être' comme 'débit'"
|
||||
"Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'","Le solde du compte déjà en débit, vous n'êtes pas autorisé à définir 'équilibre doit être' comme 'Crédit'"
|
||||
Account for the warehouse (Perpetual Inventory) will be created under this Account.,Compte de l'entrepôt ( de l'inventaire permanent ) sera créé sous ce compte .
|
||||
Account head {0} created,Employé soulagé sur {0} doit être défini comme «gauche»
|
||||
Account must be a balance sheet account,arrhes
|
||||
Account head {0} created,Responsable du compte {0} a été crée
|
||||
Account must be a balance sheet account,Le compte doit être un bilan
|
||||
Account with child nodes cannot be converted to ledger,Liste des prix non sélectionné
|
||||
Account with existing transaction can not be converted to group.,{0} n'est pas un congé approbateur valide
|
||||
Account with existing transaction can not be deleted,Compte avec la transaction existante ne peut pas être supprimé
|
||||
Account with existing transaction cannot be converted to ledger,Compte avec la transaction existante ne peut pas être converti en livre
|
||||
Account {0} cannot be a Group,Compte {0} ne peut pas être un groupe
|
||||
Account {0} does not belong to Company {1},{0} créé
|
||||
Account {0} does not belong to Company {1},Compte {0} n'appartient pas à la société {1}
|
||||
Account {0} does not belong to company: {1},Compte {0} n'appartient pas à l'entreprise: {1}
|
||||
Account {0} does not exist,Votre adresse e-mail
|
||||
Account {0} does not exist,Compte {0} n'existe pas
|
||||
Account {0} has been entered more than once for fiscal year {1},S'il vous plaît entrer « Répétez le jour du Mois de la« valeur de champ
|
||||
Account {0} is frozen,Attention: Commande {0} existe déjà contre le même numéro de bon de commande
|
||||
Account {0} is inactive,dépenses directes
|
||||
@@ -80,13 +81,13 @@ Account {0}: Parent account {1} does not belong to company: {2},Compte {0}: comp
|
||||
Account {0}: Parent account {1} does not exist,Compte {0}: compte de Parent {1} n'existe pas
|
||||
Account {0}: You can not assign itself as parent account,Compte {0}: Vous ne pouvez pas lui attribuer que compte parent
|
||||
Account: {0} can only be updated via \ Stock Transactions,Compte: {0} ne peut être mise à jour via \ Transactions de stock
|
||||
Accountant,comptable
|
||||
Accountant,Comptable
|
||||
Accounting,Comptabilité
|
||||
"Accounting Entries can be made against leaf nodes, called","Écritures comptables peuvent être faites contre nœuds feuilles , appelé"
|
||||
"Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.","Saisie comptable gelé jusqu'à cette date, personne ne peut faire / modifier entrée sauf rôle spécifié ci-dessous."
|
||||
Accounting journal entries.,Les écritures comptables.
|
||||
Accounts,Comptes
|
||||
Accounts Browser,comptes navigateur
|
||||
Accounts Browser,Navigateur des comptes
|
||||
Accounts Frozen Upto,Jusqu'à comptes gelés
|
||||
Accounts Payable,Comptes à payer
|
||||
Accounts Receivable,Débiteurs
|
||||
@@ -113,28 +114,28 @@ Actual Start Date,Date de début réelle
|
||||
Add,Ajouter
|
||||
Add / Edit Taxes and Charges,Ajouter / Modifier Taxes et frais
|
||||
Add Child,Ajouter un enfant
|
||||
Add Serial No,Ajouter N ° de série
|
||||
Add Serial No,Ajouter Numéro de série
|
||||
Add Taxes,Ajouter impôts
|
||||
Add Taxes and Charges,Ajouter Taxes et frais
|
||||
Add or Deduct,Ajouter ou déduire
|
||||
Add rows to set annual budgets on Accounts.,Ajoutez des lignes d'établir des budgets annuels des comptes.
|
||||
Add to Cart,ERP open source construit pour le web
|
||||
Add to calendar on this date,Ajouter au calendrier à cette date
|
||||
Add rows to set annual budgets on Accounts.,Ajoutez des lignes pour établir des budgets annuels sur des comptes.
|
||||
Add to Cart,Ajouter au panier
|
||||
Add to calendar on this date,Ajouter cette date au calendrier
|
||||
Add/Remove Recipients,Ajouter / supprimer des destinataires
|
||||
Address,Adresse
|
||||
Address & Contact,Adresse et coordonnées
|
||||
Address & Contacts,Adresse & Contacts
|
||||
Address & Contacts,Adresse & Coordonnées
|
||||
Address Desc,Adresse Desc
|
||||
Address Details,Détails de l'adresse
|
||||
Address HTML,Adresse HTML
|
||||
Address Line 1,Adresse ligne 1
|
||||
Address Line 2,Adresse ligne 2
|
||||
Address Template,Adresse modèle
|
||||
Address Title,Titre Adresse
|
||||
Address Title,Titre de l'adresse
|
||||
Address Title is mandatory.,Vous n'êtes pas autorisé à imprimer ce document
|
||||
Address Type,Type d'adresse
|
||||
Address master.,Ou créés par
|
||||
Administrative Expenses,applicabilité
|
||||
Address master.,Adresse principale
|
||||
Administrative Expenses,Dépenses administratives
|
||||
Administrative Officer,de l'administration
|
||||
Advance Amount,Montant de l'avance
|
||||
Advance amount,Montant de l'avance
|
||||
@@ -142,7 +143,7 @@ Advances,Avances
|
||||
Advertisement,Publicité
|
||||
Advertising,publicité
|
||||
Aerospace,aérospatial
|
||||
After Sale Installations,Après Installations Vente
|
||||
After Sale Installations,Installations Après Vente
|
||||
Against,Contre
|
||||
Against Account,Contre compte
|
||||
Against Bill {0} dated {1},Courriel invalide : {0}
|
||||
@@ -197,7 +198,7 @@ Allocated amount can not greater than unadusted amount,Montant alloué ne peut p
|
||||
Allow Bill of Materials,Laissez Bill of Materials
|
||||
Allow Bill of Materials should be 'Yes'. Because one or many active BOMs present for this item,Commande {0} n'est pas valide
|
||||
Allow Children,permettre aux enfants
|
||||
Allow Dropbox Access,Autoriser l'accès Dropbox
|
||||
Allow Dropbox Access,Autoriser l'accès au Dropbox
|
||||
Allow Google Drive Access,Autoriser Google Drive accès
|
||||
Allow Negative Balance,Laissez solde négatif
|
||||
Allow Negative Stock,Laissez Stock Négatif
|
||||
@@ -219,7 +220,7 @@ An Customer exists with same name,Il existe un client avec le même nom
|
||||
"An Item Group exists with same name, please change the item name or rename the item group","Un groupe d'objet existe avec le même nom , s'il vous plaît changer le nom de l'élément ou de renommer le groupe de l'article"
|
||||
"An item exists with same name ({0}), please change the item group name or rename the item",Le compte doit être un compte de bilan
|
||||
Analyst,analyste
|
||||
Annual,Nomenclature
|
||||
Annual,Annuel
|
||||
Another Period Closing Entry {0} has been made after {1},Point Wise impôt Détail
|
||||
Another Salary Structure {0} is active for employee {0}. Please make its status 'Inactive' to proceed.,Le taux de conversion ne peut pas être égal à 0 ou 1
|
||||
"Any other comments, noteworthy effort that should go in the records.","D'autres commentaires, l'effort remarquable qui devrait aller dans les dossiers."
|
||||
@@ -288,20 +289,20 @@ Automatically extract Job Applicants from a mail box ,
|
||||
Automatically extract Leads from a mail box e.g.,Extraire automatiquement des prospects à partir d'une boîte aux lettres par exemple
|
||||
Automatically updated via Stock Entry of type Manufacture/Repack,Automatiquement mis à jour via l'entrée de fabrication de type Stock / Repack
|
||||
Automotive,automobile
|
||||
Autoreply when a new mail is received,Autoreply quand un nouveau message est reçu
|
||||
Autoreply when a new mail is received,Réponse automatique lorsqu'un nouveau message est reçu
|
||||
Available,disponible
|
||||
Available Qty at Warehouse,Qté disponible à l'entrepôt
|
||||
Available Stock for Packing Items,Disponible en stock pour l'emballage Articles
|
||||
"Available in BOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet","Disponible en nomenclature , bon de livraison , facture d'achat , ordre de production, bon de commande , bon de réception , la facture de vente , Sales Order , Stock entrée , des feuilles de temps"
|
||||
Average Age,moyen âge
|
||||
Average Commission Rate,Taux moyen Commission
|
||||
Average Discount,D'actualisation moyen
|
||||
Awesome Products,"Pour suivre le nom de la marque dans la note qui suit documents de livraison , Opportunité , Demande de Matériel , article , bon de commande, bon d'achat, l'acheteur réception , offre , facture de vente , ventes BOM , Sales Order , No de série"
|
||||
Awesome Services,Restrictions d'autorisation de l'utilisateur
|
||||
BOM Detail No,Détail BOM Non
|
||||
Average Age,âge moyen
|
||||
Average Commission Rate,Taux moyen de la commission
|
||||
Average Discount,Remise moyenne
|
||||
Awesome Products,Produits impressionnants
|
||||
Awesome Services,Services impressionnants
|
||||
BOM Detail No,Numéro du détail BOM
|
||||
BOM Explosion Item,Article éclatement de la nomenclature
|
||||
BOM Item,Article BOM
|
||||
BOM No,Aucune nomenclature
|
||||
BOM No,Numéro BOM
|
||||
BOM No. for a Finished Good Item,N ° nomenclature pour un produit fini Bonne
|
||||
BOM Operation,Opération BOM
|
||||
BOM Operations,Opérations de nomenclature
|
||||
@@ -314,50 +315,50 @@ BOM {0} for Item {1} in row {2} is inactive or not submitted,Dépenses de voyage
|
||||
BOM {0} is not active or not submitted,Eléments requis
|
||||
BOM {0} is not submitted or inactive BOM for Item {1},BOM {0} n'est pas soumis ou inactif nomenclature pour objet {1}
|
||||
Backup Manager,Gestionnaire de sauvegarde
|
||||
Backup Right Now,Sauvegarde Right Now
|
||||
Backup Right Now,Sauvegarder immédiatement
|
||||
Backups will be uploaded to,Les sauvegardes seront téléchargées sur
|
||||
Balance Qty,solde Quantité
|
||||
Balance Sheet,Fournisseur Type maître .
|
||||
Balance Value,Valeur de balance
|
||||
Balance for Account {0} must always be {1},Point {0} avec Serial Non {1} est déjà installé
|
||||
Balance must be,avec des grands livres
|
||||
"Balances of Accounts of type ""Bank"" or ""Cash""",Date de vieillissement est obligatoire pour l'ouverture d'entrée
|
||||
Balance Qty,Qté soldée
|
||||
Balance Sheet,Bilan
|
||||
Balance Value,Valeur du solde
|
||||
Balance for Account {0} must always be {1},Solde pour le compte {0} doit toujours être {1}
|
||||
Balance must be,Solde doit être
|
||||
"Balances of Accounts of type ""Bank"" or ""Cash""","Solde du compte de type ""Banque"" ou ""Espèces"""
|
||||
Bank,Banque
|
||||
Bank / Cash Account,Banque / Compte de trésorerie
|
||||
Bank A/C No.,Bank A / C No.
|
||||
Bank / Cash Account,Compte en Banque / trésorerie
|
||||
Bank A/C No.,No. de compte bancaire
|
||||
Bank Account,Compte bancaire
|
||||
Bank Account No.,N ° de compte bancaire
|
||||
Bank Account No.,No. de compte bancaire
|
||||
Bank Accounts,Comptes bancaires
|
||||
Bank Clearance Summary,Banque Résumé de dégagement
|
||||
Bank Clearance Summary,Résumé de l'approbation de la banque
|
||||
Bank Draft,Projet de la Banque
|
||||
Bank Name,Nom de la banque
|
||||
Bank Overdraft Account,Inspection de la qualité requise pour objet {0}
|
||||
Bank Overdraft Account,Compte du découvert bancaire
|
||||
Bank Reconciliation,Rapprochement bancaire
|
||||
Bank Reconciliation Detail,Détail de rapprochement bancaire
|
||||
Bank Reconciliation Statement,État de rapprochement bancaire
|
||||
Bank Voucher,Bon Banque
|
||||
Bank/Cash Balance,Banque / Balance trésorerie
|
||||
Banking,bancaire
|
||||
Bank Reconciliation Detail,Détail du rapprochement bancaire
|
||||
Bank Reconciliation Statement,Énoncé de rapprochement bancaire
|
||||
Bank Voucher,Coupon de la banque
|
||||
Bank/Cash Balance,Solde de la banque / trésorerie
|
||||
Banking,Bancaire
|
||||
Barcode,Barcode
|
||||
Barcode {0} already used in Item {1},Lettre d'information a déjà été envoyé
|
||||
Barcode {0} already used in Item {1},Le code barre {0} est déjà utilisé dans l'article {1}
|
||||
Based On,Basé sur
|
||||
Basic,de base
|
||||
Basic Info,Informations de base
|
||||
Basic Information,Renseignements de base
|
||||
Basic Rate,Taux de base
|
||||
Basic Rate (Company Currency),Taux de base (Société Monnaie)
|
||||
Basic Rate (Company Currency),Taux de base (Monnaie de la Société )
|
||||
Batch,Lot
|
||||
Batch (lot) of an Item.,Batch (lot) d'un élément.
|
||||
Batch Finished Date,Date de lot fini
|
||||
Batch ID,ID du lot
|
||||
Batch No,Aucun lot
|
||||
Batch Started Date,Date de démarrage du lot
|
||||
Batch (lot) of an Item.,Lot d'une article.
|
||||
Batch Finished Date,La date finie d'un lot
|
||||
Batch ID,Identifiant du lot
|
||||
Batch No,Numéro du lot
|
||||
Batch Started Date,Date de début du lot
|
||||
Batch Time Logs for billing.,Temps de lots des journaux pour la facturation.
|
||||
Batch-Wise Balance History,Discontinu Histoire de la balance
|
||||
Batched for Billing,Par lots pour la facturation
|
||||
Better Prospects,De meilleures perspectives
|
||||
Bill Date,Bill Date
|
||||
Bill No,Le projet de loi no
|
||||
Bill Date,Date de la facture
|
||||
Bill No,Numéro de la facture
|
||||
Bill No {0} already booked in Purchase Invoice {1},Centre de coûts de transactions existants ne peut pas être converti en livre
|
||||
Bill of Material,De la valeur doit être inférieure à la valeur à la ligne {0}
|
||||
Bill of Material to be considered for manufacturing,Bill of Material être considéré pour la fabrication
|
||||
@@ -386,22 +387,22 @@ Both Warehouse must belong to same Company,Les deux Entrepôt doit appartenir à
|
||||
Box,boîte
|
||||
Branch,Branche
|
||||
Brand,Marque
|
||||
Brand Name,Nom de marque
|
||||
Brand Name,La marque
|
||||
Brand master.,Marque maître.
|
||||
Brands,Marques
|
||||
Breakdown,Panne
|
||||
Broadcasting,radiodiffusion
|
||||
Broadcasting,Diffusion
|
||||
Brokerage,courtage
|
||||
Budget,Budget
|
||||
Budget Allocated,Budget alloué
|
||||
Budget Detail,Détail du budget
|
||||
Budget Details,Détails du budget
|
||||
Budget Distribution,Répartition du budget
|
||||
Budget Distribution Detail,Détail Répartition du budget
|
||||
Budget Distribution Detail,Détail de la répartition du budget
|
||||
Budget Distribution Details,Détails de la répartition du budget
|
||||
Budget Variance Report,Rapport sur les écarts de budget
|
||||
Budget Variance Report,Rapport sur les écarts du budget
|
||||
Budget cannot be set for Group Cost Centers,Imprimer et stationnaire
|
||||
Build Report,construire Rapport
|
||||
Build Report,Créer un rapport
|
||||
Bundle items at time of sale.,Regrouper des envois au moment de la vente.
|
||||
Business Development Manager,Directeur du développement des affaires
|
||||
Buying,Achat
|
||||
@@ -501,7 +502,7 @@ Cheque Date,Date de chèques
|
||||
Cheque Number,Numéro de chèque
|
||||
Child account exists for this account. You can not delete this account.,Les matières premières ne peut pas être le même que l'article principal
|
||||
City,Ville
|
||||
City/Town,Ville /
|
||||
City/Town,Ville
|
||||
Claim Amount,Montant réclamé
|
||||
Claims for company expense.,Les réclamations pour frais de la société.
|
||||
Class / Percentage,Classe / Pourcentage
|
||||
@@ -561,11 +562,11 @@ Complete,Compléter
|
||||
Complete Setup,congé de maladie
|
||||
Completed,Terminé
|
||||
Completed Production Orders,Terminé les ordres de fabrication
|
||||
Completed Qty,Complété Quantité
|
||||
Completed Qty,Quantité complétée
|
||||
Completion Date,Date d'achèvement
|
||||
Completion Status,L'état d'achèvement
|
||||
Computer,ordinateur
|
||||
Computers,Informatique
|
||||
Computers,Ordinateurs
|
||||
Confirmation Date,date de confirmation
|
||||
Confirmed orders from Customers.,Confirmé commandes provenant de clients.
|
||||
Consider Tax or Charge for,Prenons l'impôt ou charge pour
|
||||
@@ -715,7 +716,7 @@ Customize the introductory text that goes as a part of that email. Each transact
|
||||
DN Detail,Détail DN
|
||||
Daily,Quotidien
|
||||
Daily Time Log Summary,Daily Time Sommaire du journal
|
||||
Database Folder ID,Database ID du dossier
|
||||
Database Folder ID,Identifiant du dossier de la base de données
|
||||
Database of potential customers.,Base de données de clients potentiels.
|
||||
Date,Date
|
||||
Date Format,Format de date
|
||||
@@ -745,7 +746,7 @@ Deductions,Déductions
|
||||
Default,Par défaut
|
||||
Default Account,Compte par défaut
|
||||
Default Address Template cannot be deleted,Adresse par défaut modèle ne peut pas être supprimé
|
||||
Default Amount,Montant en défaut
|
||||
Default Amount,Montant par défaut
|
||||
Default BOM,Nomenclature par défaut
|
||||
Default Bank / Cash account will be automatically updated in POS Invoice when this mode is selected.,Par défaut Banque / argent compte sera automatiquement mis à jour dans la facture POS lorsque ce mode est sélectionné.
|
||||
Default Bank Account,Compte bancaire par défaut
|
||||
@@ -779,18 +780,18 @@ Default settings for selling transactions.,principal
|
||||
Default settings for stock transactions.,minute
|
||||
Defense,défense
|
||||
"Define Budget for this Cost Center. To set budget action, see <a href=""#!List/Company"">Company Master</a>","Définir le budget pour ce centre de coûts. Pour définir l'action budgétaire, voir <a href=""#!List/Company"">Maître Société</a>"
|
||||
Del,Eff
|
||||
Delete,Effacer
|
||||
Del,Suppr
|
||||
Delete,Supprimer
|
||||
Delete {0} {1}?,Supprimer {0} {1} ?
|
||||
Delivered,Livré
|
||||
Delivered Items To Be Billed,Les articles livrés être facturé
|
||||
Delivered Items To Be Billed,Les items livrés à être facturés
|
||||
Delivered Qty,Qté livrée
|
||||
Delivered Serial No {0} cannot be deleted,médical
|
||||
Delivery Date,Date de livraison
|
||||
Delivery Details,Détails de la livraison
|
||||
Delivery Document No,Pas de livraison de documents
|
||||
Delivery Document Type,Type de document de livraison
|
||||
Delivery Note,Remarque livraison
|
||||
Delivery Note,Bon de livraison
|
||||
Delivery Note Item,Point de Livraison
|
||||
Delivery Note Items,Articles bordereau de livraison
|
||||
Delivery Note Message,Note Message de livraison
|
||||
@@ -800,9 +801,9 @@ Delivery Note Trends,Bordereau de livraison Tendances
|
||||
Delivery Note {0} is not submitted,Livraison Remarque {0} n'est pas soumis
|
||||
Delivery Note {0} must not be submitted,Pas de clients ou fournisseurs Comptes trouvé
|
||||
Delivery Notes {0} must be cancelled before cancelling this Sales Order,Quantité en ligne {0} ( {1} ) doit être la même que la quantité fabriquée {2}
|
||||
Delivery Status,Delivery Status
|
||||
Delivery Time,Délai de livraison
|
||||
Delivery To,Pour livraison
|
||||
Delivery Status,Statut de la livraison
|
||||
Delivery Time,L'heure de la livraison
|
||||
Delivery To,Livrer à
|
||||
Department,Département
|
||||
Department Stores,Grands Magasins
|
||||
Depends on LWP,Dépend de LWP
|
||||
@@ -822,8 +823,8 @@ Direct Income,Choisissez votre langue
|
||||
Disable,"Groupe ajoutée, rafraîchissant ..."
|
||||
Disable Rounded Total,Désactiver totale arrondie
|
||||
Disabled,Handicapé
|
||||
Discount %,% De réduction
|
||||
Discount %,% De réduction
|
||||
Discount %,% Remise
|
||||
Discount %,% Remise
|
||||
Discount (%),Remise (%)
|
||||
Discount Amount,S'il vous plaît tirer des articles de livraison Note
|
||||
"Discount Fields will be available in Purchase Order, Purchase Receipt, Purchase Invoice","Les champs d'actualisation sera disponible en commande, reçu d'achat, facture d'achat"
|
||||
@@ -1464,8 +1465,8 @@ Journal Voucher {0} does not have account {1} or already matched,Journal Bon {0}
|
||||
Journal Vouchers {0} are un-linked,Date de livraison prévue ne peut pas être avant ventes Date de commande
|
||||
Keep a track of communication related to this enquiry which will help for future reference.,Gardez une trace de la communication liée à cette enquête qui aidera pour référence future.
|
||||
Keep it web friendly 900px (w) by 100px (h),Gardez web 900px amical ( w) par 100px ( h )
|
||||
Key Performance Area,Zone de performance clé
|
||||
Key Responsibility Area,Secteur de responsabilité clé
|
||||
Key Performance Area,Section de performance clé
|
||||
Key Responsibility Area,Section à responsabilité importante
|
||||
Kg,kg
|
||||
LR Date,LR Date
|
||||
LR No,LR Non
|
||||
@@ -1723,9 +1724,9 @@ Net Weight UOM,Emballage Poids Net
|
||||
Net Weight of each Item,Poids net de chaque article
|
||||
Net pay cannot be negative,Landed Cost correctement mis à jour
|
||||
Never,Jamais
|
||||
New ,
|
||||
New ,Nouveau
|
||||
New Account,nouveau compte
|
||||
New Account Name,Nouveau compte Nom
|
||||
New Account Name,Nom du nouveau compte
|
||||
New BOM,Nouvelle nomenclature
|
||||
New Communications,Communications Nouveau-
|
||||
New Company,nouvelle entreprise
|
||||
@@ -1761,11 +1762,11 @@ Newspaper Publishers,Éditeurs de journaux
|
||||
Next,Nombre purchse de commande requis pour objet {0}
|
||||
Next Contact By,Suivant Par
|
||||
Next Contact Date,Date Contact Suivant
|
||||
Next Date,Date d'
|
||||
Next Date,Date suivante
|
||||
Next email will be sent on:,Email sera envoyé le:
|
||||
No,Aucun
|
||||
No,Non
|
||||
No Customer Accounts found.,Aucun client ne représente trouvés.
|
||||
No Customer or Supplier Accounts found,Encaisse
|
||||
No Customer or Supplier Accounts found,Aucun compte client ou fournisseur trouvé
|
||||
No Expense Approvers. Please assign 'Expense Approver' Role to atleast one user,Compte {0} est inactif
|
||||
No Item with Barcode {0},Bon de commande {0} ' arrêté '
|
||||
No Item with Serial No {0},non autorisé
|
||||
@@ -1775,13 +1776,13 @@ No Permission,Aucune autorisation
|
||||
No Production Orders created,Section de base
|
||||
No Supplier Accounts found. Supplier Accounts are identified based on 'Master Type' value in account record.,Aucun fournisseur ne représente trouvés. Comptes fournisseurs sont identifiés sur la base de la valeur «Maître Type ' dans le compte rendu.
|
||||
No accounting entries for the following warehouses,Pas d'entrées comptables pour les entrepôts suivants
|
||||
No addresses created,Aucune adresse créés
|
||||
No addresses created,Aucune adresse créée
|
||||
No contacts created,Pas de contacts créés
|
||||
No default Address Template found. Please create a new one from Setup > Printing and Branding > Address Template.,Aucune valeur par défaut Adresse modèle trouvé. S'il vous plaît créer un nouveau à partir de Configuration> Presse et Branding> Adresse modèle.
|
||||
No default BOM exists for Item {0},services impressionnants
|
||||
No description given,Le jour (s ) sur lequel vous postulez pour congé sont les vacances . Vous n'avez pas besoin de demander l'autorisation .
|
||||
No employee found,Aucun employé
|
||||
No employee found!,Aucun employé !
|
||||
No employee found,Aucun employé trouvé
|
||||
No employee found!,Aucun employé trouvé!
|
||||
No of Requested SMS,Pas de SMS demandés
|
||||
No of Sent SMS,Pas de SMS envoyés
|
||||
No of Visits,Pas de visites
|
||||
@@ -1816,14 +1817,14 @@ Note: This Cost Center is a Group. Cannot make accounting entries against groups
|
||||
Note: {0},Compte avec des nœuds enfants ne peut pas être converti en livre
|
||||
Notes,Remarques
|
||||
Notes:,notes:
|
||||
Nothing to request,Rien à demander
|
||||
Nothing to request,Pas de requête à demander
|
||||
Notice (days),Avis ( jours )
|
||||
Notification Control,Contrôle de notification
|
||||
Notification Email Address,Adresse e-mail de notification
|
||||
Notify by Email on creation of automatic Material Request,Notification par courriel lors de la création de la demande de matériel automatique
|
||||
Number Format,Format numérique
|
||||
Offer Date,offre date
|
||||
Office,Fonction
|
||||
Offer Date,Date de l'offre
|
||||
Office,Bureau
|
||||
Office Equipments,Équipement de bureau
|
||||
Office Maintenance Expenses,Date d'adhésion doit être supérieure à Date de naissance
|
||||
Office Rent,POS Global Setting {0} déjà créé pour la compagnie {1}
|
||||
@@ -1970,7 +1971,7 @@ Payments made during the digest period,Les paiements effectués au cours de la p
|
||||
Payments received during the digest period,Les paiements reçus au cours de la période digest
|
||||
Payroll Settings,Paramètres de la paie
|
||||
Pending,En attendant
|
||||
Pending Amount,Montant attente
|
||||
Pending Amount,Montant en attente
|
||||
Pending Items {0} updated,Machines et installations
|
||||
Pending Review,Attente d'examen
|
||||
Pending SO Items For Purchase Request,"Articles en attente Donc, pour demande d'achat"
|
||||
@@ -2097,11 +2098,11 @@ Please set {0},S'il vous plaît mettre {0}
|
||||
Please setup Employee Naming System in Human Resource > HR Settings,S'il vous plaît configuration Naming System employés en ressources humaines> Paramètres RH
|
||||
Please setup numbering series for Attendance via Setup > Numbering Series,S'il vous plaît configuration série de numérotation à la fréquentation via Configuration> Série de numérotation
|
||||
Please setup your chart of accounts before you start Accounting Entries,S'il vous plaît configurer votre plan de comptes avant de commencer Écritures comptables
|
||||
Please specify,S'il vous plaît spécifier
|
||||
Please specify,Veuillez spécifier
|
||||
Please specify Company,S'il vous plaît préciser Company
|
||||
Please specify Company to proceed,Veuillez indiquer Société de procéder
|
||||
Please specify Default Currency in Company Master and Global Defaults,N ° de série {0} n'existe pas
|
||||
Please specify a,S'il vous plaît spécifier une
|
||||
Please specify a,Veuillez spécifier un
|
||||
Please specify a valid 'From Case No.',S'il vous plaît indiquer une valide »De Affaire n '
|
||||
Please specify a valid Row ID for {0} in row {1},Il s'agit d'un site d'exemple généré automatiquement à partir de ERPNext
|
||||
Please specify either Quantity or Valuation Rate or both,S'il vous plaît spécifier Quantité ou l'évaluation des taux ou à la fois
|
||||
@@ -2205,7 +2206,7 @@ Publishing,édition
|
||||
Pull sales orders (pending to deliver) based on the above criteria,Tirez les ordres de vente (en attendant de livrer) sur la base des critères ci-dessus
|
||||
Purchase,Acheter
|
||||
Purchase / Manufacture Details,Achat / Fabrication Détails
|
||||
Purchase Analytics,Achat Analytics
|
||||
Purchase Analytics,Les analyses des achats
|
||||
Purchase Common,Achat commune
|
||||
Purchase Details,Conditions de souscription
|
||||
Purchase Discounts,Rabais sur l'achat
|
||||
@@ -2256,7 +2257,7 @@ Qty To Manufacture,Quantité à fabriquer
|
||||
Qty as per Stock UOM,Qté en stock pour Emballage
|
||||
Qty to Deliver,Quantité à livrer
|
||||
Qty to Order,Quantité à commander
|
||||
Qty to Receive,Quantité pour recevoir
|
||||
Qty to Receive,Quantité à recevoir
|
||||
Qty to Transfer,Quantité de Transfert
|
||||
Qualification,Qualification
|
||||
Quality,Qualité
|
||||
@@ -2278,11 +2279,11 @@ Quantity required for Item {0} in row {1},Quantité requise pour objet {0} à la
|
||||
Quarter,Trimestre
|
||||
Quarterly,Trimestriel
|
||||
Quick Help,Aide rapide
|
||||
Quotation,Citation
|
||||
Quotation,Devis
|
||||
Quotation Item,Article devis
|
||||
Quotation Items,Articles de devis
|
||||
Quotation Lost Reason,Devis perdu la raison
|
||||
Quotation Message,Devis message
|
||||
Quotation Message,Message du devis
|
||||
Quotation To,Devis Pour
|
||||
Quotation Trends,Devis Tendances
|
||||
Quotation {0} is cancelled,Devis {0} est annulée
|
||||
@@ -2296,8 +2297,8 @@ Random,Aléatoire
|
||||
Range,Gamme
|
||||
Rate,Taux
|
||||
Rate ,Taux
|
||||
Rate (%),S'il vous plaît entrer la date soulager .
|
||||
Rate (Company Currency),Taux (Société Monnaie)
|
||||
Rate (%),Taux (%)
|
||||
Rate (Company Currency),Taux (Monnaie de la société)
|
||||
Rate Of Materials Based On,Taux de matériaux à base
|
||||
Rate and Amount,Taux et le montant
|
||||
Rate at which Customer Currency is converted to customer's base currency,Vitesse à laquelle la devise du client est converti en devise de base du client
|
||||
@@ -2318,15 +2319,15 @@ Re-order Level,Re-order niveau
|
||||
Re-order Qty,Re-order Quantité
|
||||
Read,Lire
|
||||
Reading 1,Lecture 1
|
||||
Reading 10,Lecture le 10
|
||||
Reading 10,Lecture 10
|
||||
Reading 2,Lecture 2
|
||||
Reading 3,Reading 3
|
||||
Reading 4,Reading 4
|
||||
Reading 5,Reading 5
|
||||
Reading 6,Lecture 6
|
||||
Reading 7,Lecture le 7
|
||||
Reading 7,Lecture 7
|
||||
Reading 8,Lecture 8
|
||||
Reading 9,Lectures suggérées 9
|
||||
Reading 9,Lecture 9
|
||||
Real Estate,Immobilier
|
||||
Reason,Raison
|
||||
Reason for Leaving,Raison du départ
|
||||
@@ -2345,7 +2346,7 @@ Received and Accepted,Reçus et acceptés
|
||||
Receiver List,Liste des récepteurs
|
||||
Receiver List is empty. Please create Receiver List,Soit quantité de cible ou le montant cible est obligatoire .
|
||||
Receiver Parameter,Paramètre récepteur
|
||||
Recipients,Récipiendaires
|
||||
Recipients,Destinataires
|
||||
Reconcile,réconcilier
|
||||
Reconciliation Data,Données de réconciliation
|
||||
Reconciliation HTML,Réconciliation HTML
|
||||
@@ -2383,7 +2384,7 @@ Remarks,Remarques
|
||||
Remarks Custom,Remarques sur commande
|
||||
Rename,rebaptiser
|
||||
Rename Log,Renommez identifiez-vous
|
||||
Rename Tool,Renommer l'outil
|
||||
Rename Tool,Outils de renommage
|
||||
Rent Cost,louer coût
|
||||
Rent per hour,Louer par heure
|
||||
Rented,Loué
|
||||
@@ -2419,8 +2420,8 @@ Reseller,Revendeur
|
||||
Reserved,réservé
|
||||
Reserved Qty,Quantité réservés
|
||||
"Reserved Qty: Quantity ordered for sale, but not delivered.","Réservés Quantité: Quantité de commande pour la vente , mais pas livré ."
|
||||
Reserved Quantity,Quantité réservés
|
||||
Reserved Warehouse,Réservé Entrepôt
|
||||
Reserved Quantity,Quantité réservée
|
||||
Reserved Warehouse,Entrepôt réservé
|
||||
Reserved Warehouse in Sales Order / Finished Goods Warehouse,Entrepôt réservé à des commandes clients / entrepôt de produits finis
|
||||
Reserved Warehouse is missing in Sales Order,Réservé entrepôt est manquant dans l'ordre des ventes
|
||||
Reserved Warehouse required for stock Item {0} in row {1},Centre de coûts par défaut de vente
|
||||
@@ -3158,8 +3159,8 @@ Voucher Type and Date,Type de chèques et date
|
||||
Walk In,Walk In
|
||||
Warehouse,entrepôt
|
||||
Warehouse Contact Info,Entrepôt Info Contact
|
||||
Warehouse Detail,Détail d'entrepôt
|
||||
Warehouse Name,Nom d'entrepôt
|
||||
Warehouse Detail,Détail de l'entrepôt
|
||||
Warehouse Name,Nom de l'entrepôt
|
||||
Warehouse and Reference,Entrepôt et référence
|
||||
Warehouse can not be deleted as stock ledger entry exists for this warehouse.,Descendre : {0}
|
||||
Warehouse can only be changed via Stock Entry / Delivery Note / Purchase Receipt,Entrepôt ne peut être modifié via Stock Entrée / bon de livraison / reçu d'achat
|
||||
@@ -3177,15 +3178,15 @@ Warehouse {0}: Parent account {1} does not bolong to the company {2},Entrepôt {
|
||||
Warehouse-Wise Stock Balance,Warehouse-Wise Stock Solde
|
||||
Warehouse-wise Item Reorder,Warehouse-sage Réorganiser article
|
||||
Warehouses,Entrepôts
|
||||
Warehouses.,applicable
|
||||
Warehouses.,Entrepôts.
|
||||
Warn,Avertir
|
||||
Warning: Leave application contains following block dates,Attention: la demande d'autorisation contient les dates de blocs suivants
|
||||
Warning: Material Requested Qty is less than Minimum Order Qty,Attention: Matériel requis Quantité est inférieure Quantité minimum à commander
|
||||
Warning: Sales Order {0} already exists against same Purchase Order number,S'il vous plaît vérifier ' Est Advance' contre compte {0} si c'est une entrée avance .
|
||||
Warning: System will not check overbilling since amount for Item {0} in {1} is zero,Attention : Le système ne vérifie pas la surfacturation depuis montant pour objet {0} dans {1} est nulle
|
||||
Warranty / AMC Details,Garantie / AMC Détails
|
||||
Warranty / AMC Status,Garantie / AMC Statut
|
||||
Warranty Expiry Date,Date d'expiration de garantie
|
||||
Warranty / AMC Details,Garantie / Détails AMC
|
||||
Warranty / AMC Status,Garantie / Statut AMC
|
||||
Warranty Expiry Date,Date d'expiration de la garantie
|
||||
Warranty Period (Days),Période de garantie (jours)
|
||||
Warranty Period (in days),Période de garantie (en jours)
|
||||
We buy this Item,Nous achetons cet article
|
||||
@@ -3237,7 +3238,7 @@ Write Off Outstanding Amount,Ecrire Off Encours
|
||||
Write Off Voucher,Ecrire Off Bon
|
||||
Wrong Template: Unable to find head row.,Modèle tort: Impossible de trouver la ligne de tête.
|
||||
Year,Année
|
||||
Year Closed,L'année Fermé
|
||||
Year Closed,L'année est fermée
|
||||
Year End Date,Fin de l'exercice Date de
|
||||
Year Name,Nom Année
|
||||
Year Start Date,Date de début Année
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user