diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/unverified/tr_l10ntr_tek_duzen_hesap.json b/erpnext/accounts/doctype/account/chart_of_accounts/unverified/tr_l10ntr_tek_duzen_hesap.json
deleted file mode 100644
index dfc821eb53e..00000000000
--- a/erpnext/accounts/doctype/account/chart_of_accounts/unverified/tr_l10ntr_tek_duzen_hesap.json
+++ /dev/null
@@ -1,531 +0,0 @@
-{
- "country_code": "tr",
- "name": "Turkey - Tek D\u00fczen Hesap Plan\u0131",
- "tree": {
- "Duran Varl\u0131klar": {
- "Di\u011fer Alacaklar": {
- "Ba\u011fl\u0131 Ortakl\u0131klardan Alacaklar": {},
- "Di\u011fer Alacak Senetleri Reeskontu(-)": {},
- "Di\u011fer \u00c7e\u015fitli Alacaklar": {},
- "Ortaklardan Alacaklar": {},
- "Personelden Alacaklar": {},
- "\u0130\u015ftiraklerden Alacaklar": {},
- "\u015e\u00fcpheli Di\u011fer Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
- },
- "Di\u011fer Duran Varl\u0131klar": {
- "Birikmi\u015f Amortismanlar(-)": {},
- "Di\u011fer KDV": {},
- "Di\u011fer \u00c7e\u015fitli Duran Varl\u0131klar": {},
- "Elden \u00c7\u0131kar\u0131lacak Stoklar Ve Maddi Duran Varl\u0131klar": {},
- "Gelecek Y\u0131llar \u0130htiyac\u0131 Stoklar": {},
- "Gelecek Y\u0131llarda \u0130ndirilecek KDV": {},
- "Pe\u015fin \u00d6denen Vergi Ve Fonlar": {},
- "Stok De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
- },
- "Gelecek Y\u0131llara Ait Giderler ve Gelir Tahakkuklar\u0131": {
- "Gelecek Y\u0131llara Ait Giderler": {},
- "Gelir Tahakkuklar\u0131": {}
- },
- "Maddi Duran Varl\u0131klar": {
- "Arazi Ve Arsalar": {},
- "Binalar": {},
- "Birikmi\u015f Amortismanlar(-)": {},
- "Demirba\u015flar": {},
- "Di\u011fer Maddi Duran Varl\u0131klar": {},
- "Ta\u015f\u0131tlar": {},
- "Tesis, Makine Ve Cihazlar": {},
- "Verilen Avanslar": {},
- "Yap\u0131lmakta Olan Yat\u0131r\u0131mlar": {},
- "Yer Alt\u0131 Ve Yer \u00dcst\u00fc D\u00fczenleri": {}
- },
- "Maddi Olmayan Duran Varl\u0131klar": {
- "Ara\u015ft\u0131rma Ve Geli\u015ftirme Giderleri": {},
- "Birikmi\u015f Amortismanlar(-)": {},
- "Di\u011fer Maddi Olmayan Duran Varl\u0131klar": {},
- "Haklar": {},
- "Kurulu\u015f Ve \u00d6rg\u00fctlenme Giderleri": {},
- "Verilen Avanslar": {},
- "\u00d6zel Maliyetler": {},
- "\u015eerefiye": {}
- },
- "Mali Duran Varl\u0131klar": {
- "Ba\u011fl\u0131 Menkul K\u0131ymetler": {},
- "Ba\u011fl\u0131 Menkul K\u0131ymetler De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
- "Ba\u011fl\u0131 Ortakl\u0131klar": {},
- "Ba\u011fl\u0131 Ortakl\u0131klar Sermaye Paylar\u0131 De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
- "Ba\u011fl\u0131 Ortakl\u0131klara Sermaye Taahh\u00fctleri(-)": {},
- "Di\u011fer Mali Duran Varl\u0131klar": {},
- "Di\u011fer Mali Duran Varl\u0131klar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
- "\u0130\u015ftirakler": {},
- "\u0130\u015ftirakler Sermaye Paylar\u0131 De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
- "\u0130\u015ftiraklere Sermaye Taahh\u00fctleri(-)": {}
- },
- "Ticari Alacaklar": {
- "Alacak Senetleri": {},
- "Alacak Senetleri Reeskontu(-)": {},
- "Al\u0131c\u0131lar": {},
- "Kazaqn\u0131lmam\u0131\u015f Finansal Kiralama Faiz Gelirleri(-)": {},
- "Verilen Depozito Ve Teminatlar": {},
- "\u015e\u00fcpheli Ticari Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
- },
- "root_type": "",
- "\u00d6zel T\u00fckenmeye Tabi Varl\u0131klar": {
- "Arama Giderleri": {},
- "Birikmi\u015f T\u00fckenme Paylar\u0131(-)": {},
- "Di\u011fer \u00d6zel T\u00fckenmeye Tabi Varl\u0131klar": {},
- "Haz\u0131rl\u0131k Ve Geli\u015ftirme Giderleri": {},
- "Verilen Avanslar": {}
- }
- },
- "D\u00f6nen Varl\u0131klar": {
- "Di\u011fer Alacaklar": {
- "Ba\u011fl\u0131 Ortakl\u0131klardan Alacaklar": {},
- "Di\u011fer Alacak Senetleri Reeskontu(-)": {},
- "Di\u011fer \u00c7e\u015fitli Alacaklar": {},
- "Ortaklardan Alacaklar": {},
- "Personelden Alacaklar": {},
- "\u0130\u015ftiraklerden Alacaklar": {},
- "\u015e\u00fcpheli Di\u011fer Alacaklar": {},
- "\u015e\u00fcpheli Di\u011fer Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {}
- },
- "Di\u011fer D\u00f6nen Varl\u0131klar": {
- "Devreden KDV": {},
- "Di\u011fer D\u00f6nen Varl\u0131klar Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
- "Di\u011fer KDV": {},
- "Di\u011fer \u00c7e\u015fitli D\u00f6nen Varl\u0131klar": {},
- "Personel Avanslar\u0131": {},
- "Pe\u015fin \u00d6denen Vergiler Ve Fonlar": {},
- "Say\u0131m Ve Tesell\u00fcm Noksanlar\u0131": {},
- "\u0130ndirilecek KDV": {},
- "\u0130\u015f Avanslar\u0131": {}
- },
- "Gelecek Aylara Ait Giderler ve Gelir Tahakkuklar\u0131": {
- "Gelecek Aylara Ait Giderler": {},
- "Gelir Tahakkuklar\u0131": {}
- },
- "Haz\u0131r De\u011ferler": {
- "Al\u0131nan \u00c7ekler": {},
- "Bankalar": {
- "account_type": "Bank"
- },
- "Di\u011fer Haz\u0131r De\u011ferler": {},
- "Kasa": {
- "account_type": "Cash"
- },
- "Verilen \u00c7ekler ve \u00d6deme Emirleri(-)": {}
- },
- "Menkul K\u0131ymetler": {
- "Di\u011fer Menkul K\u0131ymetler": {},
- "Hisse Senetleri": {},
- "Kamu Kesimi Tahvil, Senet ve Bonolar\u0131": {},
- "Menkul K\u0131ymetler De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
- "\u00d6zel Kesim Tahvil Senet Ve Bonolar\u0131": {}
- },
- "Stoklar": {
- "Mamuller": {},
- "Stok De\u011fer D\u00fc\u015f\u00fckl\u00fc\u011f\u00fc Kar\u015f\u0131l\u0131\u011f\u0131(-)": {},
- "Ticari Mallar": {},
- "Verilen Sipari\u015f Avanslar\u0131": {},
- "Yar\u0131 Mamuller": {},
- "\u0130lk Madde Malzeme": {}
- },
- "Ticari Alacaklar": {
- "Alacak Senetleri": {},
- "Alacak Senetleri Reeskontu(-)": {},
- "Al\u0131c\u0131lar": {},
- "Di\u011fer Ticari Alacaklar": {},
- "Kazan\u0131lmam\u0131\u015f Finansal Kiralama Faiz Gelirleri(-)": {},
- "Verilen Depozito ve Teminatlar": {},
- "\u015e\u00fcpheli Ticari Alacaklar": {},
- "\u015e\u00fcpheli Ticari Alacaklar Kar\u015f\u0131l\u0131\u011f\u0131": {}
- },
- "Y\u0131llara Yayg\u0131n \u0130n\u015faat ve Onar\u0131m Maliyetleri": {
- "Ta\u015feronlara Verilen Avanslar": {},
- "Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Onar\u0131m Maliyetleri": {}
- },
- "root_type": ""
- },
- "Gelir Tablosu Hesaplar\u0131": {
- "Br\u00fct Sat\u0131\u015flar": {
- "Di\u011fer Gelirler": {},
- "Yurt D\u0131\u015f\u0131 Sat\u0131\u015flar": {},
- "Yurt \u0130\u00e7i Sat\u0131\u015flar": {}
- },
- "Di\u011fer Faaliyetlerden Olu\u015fan Gelir ve K\u00e2rlar": {
- "Ba\u011fl\u0131 Ortakl\u0131klardan Temett\u00fc Gelirleri": {},
- "Di\u011fer Ola\u011fan Gelir Ve K\u00e2rlar": {},
- "Enflasyon D\u00fczeltme K\u00e2rlar\u0131": {},
- "Faiz Gelirleri": {},
- "Kambiyo K\u00e2rlar\u0131": {},
- "Komisyon Gelirleri": {},
- "Konusu Kalmayan Kar\u015f\u0131l\u0131klar": {},
- "Menkul K\u0131ymet Sat\u0131\u015f K\u00e2rlar\u0131": {},
- "Reeskont Faiz Gelirleri": {},
- "\u0130\u015ftiraklerden Temett\u00fc Gelirleri": {}
- },
- "Di\u011fer Faaliyetlerden Olu\u015fan Gider ve Zararlar (-)": {
- "Di\u011fer Ola\u011fan Gider Ve Zararlar(-)": {},
- "Enflasyon D\u00fczeltmesi Zararlar\u0131(-)": {},
- "Kambiyo Zararlar\u0131(-)": {},
- "Kar\u015f\u0131l\u0131k Giderleri(-)": {},
- "Komisyon Giderleri(-)": {},
- "Menkul K\u0131ymet Sat\u0131\u015f Zararlar\u0131(-)": {},
- "Reeskont Faiz Giderleri(-)": {}
- },
- "D\u00f6nem Net K\u00e2r\u0131 Ve Zarar\u0131": {
- "D\u00f6nem K\u00e2r\u0131 Vergi Ve Di\u011fer Yasal Y\u00fck\u00fcml\u00fcl\u00fck Kar\u015f\u0131l\u0131klar\u0131(-)": {},
- "D\u00f6nem K\u00e2r\u0131 Veya Zarar\u0131": {},
- "D\u00f6nem Net K\u00e2r\u0131 Veya Zarar\u0131": {},
- "Enflasyon D\u00fczeltme Hesab\u0131": {},
- "Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Enflasyon D\u00fczeltme Hesab\u0131": {}
- },
- "Faaliyet Giderleri(-)": {
- "Ara\u015ft\u0131rma Ve Geli\u015ftirme Giderleri(-)": {},
- "Genel Y\u00f6netim Giderleri(-)": {},
- "Pazarlama Sat\u0131\u015f Ve Da\u011f\u0131t\u0131m Giderleri(-)": {}
- },
- "Finansman Giderleri": {
- "K\u0131sa Vadeli Bor\u00e7lanma Giderleri(-)": {},
- "Uzun Vadeli Bor\u00e7lanma Giderleri(-)": {}
- },
- "Ola\u011fan D\u0131\u015f\u0131 Gelir Ve K\u00e2rlar": {
- "Di\u011fer Ola\u011fan D\u0131\u015f\u0131 Gelir Ve K\u00e2rlar": {},
- "\u00d6nceki D\u00f6nem Gelir Ve K\u00e2rlar\u0131": {}
- },
- "Ola\u011fan D\u0131\u015f\u0131 Gider Ve Zaralar(-)": {
- "Di\u011fer Ola\u011fan D\u0131\u015f\u0131 Gider Ve Zararlar(-)": {},
- "\u00c7al\u0131\u015fmayan K\u0131s\u0131m Gider Ve Zararlar\u0131(-)": {},
- "\u00d6nceki D\u00f6nem Gider Ve Zararlar\u0131(-)": {}
- },
- "Sat\u0131\u015f \u0130ndirimleri (-)": {
- "Di\u011fer \u0130ndirimler": {},
- "Sat\u0131\u015f \u0130ndirimleri(-)": {},
- "Sat\u0131\u015ftan \u0130adeler(-)": {}
- },
- "Sat\u0131\u015flar\u0131n Maliyeti(-)": {
- "Di\u011fer Sat\u0131\u015flar\u0131n Maliyeti(-)": {},
- "Sat\u0131lan Hizmet Maliyeti(-)": {},
- "Sat\u0131lan Mamuller Maliyeti(-)": {},
- "Sat\u0131lan Ticari Mallar Maliyeti(-)": {}
- },
- "root_type": ""
- },
- "K\u0131sa Vadeli Yabanc\u0131 Kaynaklar": {
- "Al\u0131nan Avanslar": {
- "Al\u0131nan Di\u011fer Avanslar": {
- "account_type": "Payable"
- },
- "Al\u0131nan Sipari\u015f Avanslar\u0131": {
- "account_type": "Payable"
- },
- "account_type": "Payable"
- },
- "Bor\u00e7 ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
- "Di\u011fer Bor\u00e7 Ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
- "account_type": "Payable"
- },
- "D\u00f6nem K\u00e2r\u0131 Vergi Ve Di\u011fer Yasal Y\u00fck\u00fcml\u00fcl\u00fck Kar\u015f\u0131l\u0131klar\u0131": {
- "account_type": "Tax"
- },
- "D\u00f6nem K\u00e2r\u0131n\u0131n Pe\u015fin \u00d6denen Vergi Ve Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler(-)": {
- "account_type": "Tax"
- },
- "K\u0131dem Tazminat\u0131 Kar\u015f\u0131l\u0131\u011f\u0131": {},
- "Maliyet Giderleri Kar\u015f\u0131l\u0131\u011f\u0131": {},
- "account_type": "Payable"
- },
- "Di\u011fer Bor\u00e7lar": {
- "Ba\u011fl\u0131 Ortakl\u0131klara Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Di\u011fer Bor\u00e7 Senetleri Reeskontu(-)": {
- "account_type": "Payable"
- },
- "Di\u011fer \u00c7e\u015fitli Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Ortaklara Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Personele Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "account_type": "Payable",
- "\u0130\u015ftiraklere Bor\u00e7lar": {
- "account_type": "Payable"
- }
- },
- "Di\u011fer K\u0131sa Vadeli Yabanc\u0131 Kaynaklar": {
- "Di\u011fer KDV": {
- "account_type": "Tax"
- },
- "Di\u011fer \u00c7e\u015fitli Yabanc\u0131 Kaynaklar": {},
- "Hesaplanan KDV": {
- "account_type": "Tax"
- },
- "Merkez Ve \u015eubeler Cari Hesab\u0131": {},
- "Say\u0131m Ve Tesell\u00fcm Fazlalar\u0131": {},
- "account_type": "Payable"
- },
- "Gelecek Aylara Ait Gelirler Ve Gider Tahakkuklar\u0131": {
- "Gelecek Aylara Ait Gelirler": {},
- "Gider Tahakkuklar\u0131": {}
- },
- "Mali Bor\u00e7lar": {
- "Banka Kredileri": {
- "account_type": "Payable"
- },
- "Di\u011fer Mali Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Ertelenmi\u015f Finansal Kiralama Bor\u00e7lanma Maliyetleri(-)": {
- "account_type": "Payable"
- },
- "Finansal Kiralama \u0130\u015flemlerinden Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Menkul K\u0131ymetler \u0130hra\u00e7 Fark\u0131(-)": {
- "account_type": "Payable"
- },
- "Tahvil Anapara Bor\u00e7, Taksit Ve Faizleri": {
- "account_type": "Payable"
- },
- "Uzun Vadeli Kredilerin Anapara Taksitleri Ve Faizleri": {
- "account_type": "Payable"
- },
- "account_type": "Payable",
- "\u00c7\u0131kar\u0131lan Bonolar Ve Senetler": {
- "account_type": "Payable"
- },
- "\u00c7\u0131kar\u0131lm\u0131\u015f Di\u011fer Menkul K\u0131ymetler": {
- "account_type": "Payable"
- }
- },
- "Ticari Bor\u00e7lar": {
- "Al\u0131nan Depozito Ve Teminatlar": {
- "account_type": "Payable"
- },
- "Bor\u00e7 Senetleri": {
- "account_type": "Payable"
- },
- "Bor\u00e7 Senetleri Reeskontu(-)": {
- "account_type": "Payable"
- },
- "Di\u011fer Ticari Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Sat\u0131c\u0131lar": {
- "account_type": "Payable"
- },
- "account_type": "Payable"
- },
- "Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Onar\u0131m Hakedi\u015fleri": {
- "350 Y\u0131llara Yayg\u0131n \u0130n\u015faat Ve Onar\u0131m Hakedi\u015fleri Bedelleri": {
- "account_type": "Payable"
- },
- "account_type": "Payable"
- },
- "root_type": "",
- "\u00d6denecek Vergi ve Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler": {
- "Vadesi Ge\u00e7mi\u015f, Ertelenmi\u015f Veya Taksitlendirilmi\u015f Vergi Ve Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler": {
- "account_type": "Tax"
- },
- "account_type": "Tax",
- "\u00d6denecek Di\u011fer Y\u00fck\u00fcml\u00fcl\u00fckler": {
- "account_type": "Tax"
- },
- "\u00d6denecek Sosyal G\u00fcvenl\u00fck Kesintileri": {
- "account_type": "Tax"
- },
- "\u00d6denecek Vergi Ve Fonlar": {
- "account_type": "Tax"
- }
- }
- },
- "Maliyet Hesaplar\u0131": {
- "Ara\u015ft\u0131rma Ve Geli\u015ftirme Giderleri": {},
- "Direkt \u0130lk Madde Ve Malzeme Giderleri": {
- "Direk \u0130lk Madde Ve Malzeme Giderleri Hesab\u0131": {},
- "Direkt \u0130lk Madde Ve Malzeme Fiyat Fark\u0131": {},
- "Direkt \u0130lk Madde Ve Malzeme Miktar Fark\u0131": {},
- "Direkt \u0130lk Madde Ve Malzeme Yans\u0131tma Hesab\u0131": {}
- },
- "Direkt \u0130\u015f\u00e7ilik Giderleri": {
- "Direkt \u0130\u015f\u00e7ilik Giderleri": {},
- "Direkt \u0130\u015f\u00e7ilik Giderleri Yans\u0131tma Hesab\u0131": {},
- "Direkt \u0130\u015f\u00e7ilik S\u00fcre Farklar\u0131": {},
- "Direkt \u0130\u015f\u00e7ilik \u00dccret Farklar\u0131": {}
- },
- "Finansman Giderleri": {
- "Finansman Giderleri": {},
- "Finansman Giderleri Fark Hesab\u0131": {},
- "Finansman Giderleri Yans\u0131tma Hesab\u0131": {}
- },
- "Genel Y\u00f6netim Giderleri": {
- "Genel Y\u00f6netim Gider Farklar\u0131 Hesab\u0131": {},
- "Genel Y\u00f6netim Giderleri": {},
- "Genel Y\u00f6netim Giderleri Yans\u0131tma Hesab\u0131": {}
- },
- "Genel \u00dcretim Giderleri": {
- "Genel \u00dcretim Giderleri": {},
- "Genel \u00dcretim Giderleri B\u00fct\u00e7e Farklar\u0131": {},
- "Genel \u00dcretim Giderleri Kapasite Farklar\u0131": {},
- "Genel \u00dcretim Giderleri Verimlilik Giderleri": {},
- "Genel \u00dcretim Giderleri Yans\u0131tma Hesab\u0131": {}
- },
- "Hizmet \u00dcretim Maliyeti": {
- "Hizmet \u00dcretim Maliyeti": {},
- "Hizmet \u00dcretim Maliyeti Fark Hesaplar\u0131": {},
- "Hizmet \u00dcretim Maliyeti Yans\u0131tma Hesab\u0131": {}
- },
- "Maliyet Muhasebesi Ba\u011flant\u0131 Hesaplar\u0131": {
- "Maliyet Muhasebesi Ba\u011flant\u0131 Hesab\u0131": {},
- "Maliyet Muhasebesi Yans\u0131tma Hesab\u0131": {}
- },
- "Pazarlama, Sat\u0131\u015f Ve Da\u011f\u0131t\u0131m Giderleri": {
- "Atra\u015ft\u0131rma Ve Geli\u015ftirme Giderleri": {},
- "Pazarlama Sat\u0131\u015f Ve Dag\u0131t\u0131m Giderleri Yans\u0131tma Hesab\u0131": {},
- "Pazarlama Sat\u0131\u015f Ve Da\u011f\u0131t\u0131m Giderleri Fark Hesab\u0131": {}
- },
- "root_type": ""
- },
- "Naz\u0131m Hesaplar": {
- "root_type": ""
- },
- "Serbest Hesaplar": {
- "root_type": ""
- },
- "Uzun Vadeli Yabanc\u0131 Kaynaklar": {
- "Al\u0131nan Avanslar": {
- "Al\u0131nan Di\u011fer Avanslar": {
- "account_type": "Payable"
- },
- "Al\u0131nan Sipari\u015f Avanslar\u0131": {
- "account_type": "Payable"
- },
- "account_type": "Payable"
- },
- "Bor\u00e7 Ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
- "Di\u011fer Bor\u00e7 Ve Gider Kar\u015f\u0131l\u0131klar\u0131": {
- "account_type": "Payable"
- },
- "K\u0131dem Tazminat\u0131 Kar\u015f\u0131l\u0131\u011f\u0131": {},
- "account_type": "Payable"
- },
- "Di\u011fer Bor\u00e7lar": {
- "Ba\u011fl\u0131 Ortakl\u0131klara Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Di\u011fer Bor\u00e7 Senetleri Reeskontu(-)": {
- "account_type": "Payable"
- },
- "Di\u011fer \u00c7e\u015fitli Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Kamuya Olan Ertelenmi\u015f Veya Taksitlendirilmi\u015f Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Ortaklara Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "account_type": "Payable",
- "\u0130\u015ftiraklere Bor\u00e7lar": {
- "account_type": "Payable"
- }
- },
- "Di\u011fer Uzun Vadeli Yabanc\u0131 Kaynaklar": {
- "Di\u011fer \u00c7e\u015fitli Uzun Vadeli Yabanc\u0131 Kaynaklar": {
- "account_type": "Payable"
- },
- "Gelecek Y\u0131llara Ertelenmi\u015f Veya Terkin Edilecek KDV": {
- "account_type": "Payable"
- },
- "Tesise Kat\u0131lma Paylar\u0131": {
- "account_type": "Payable"
- },
- "account_type": "Payable"
- },
- "Gelecek Y\u0131llara Ait Gelirler Ve Gider Tahakkuklar\u0131": {
- "Gelecek Y\u0131llara Ait Gelirler": {},
- "Gider Tahakkuklar\u0131": {}
- },
- "Mali Bor\u00e7lar": {
- "Banka Kredileri": {
- "account_type": "Payable"
- },
- "Di\u011fer Mali Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Ertelenmi\u015f Finansal Kiralama Bor\u00e7lanma Maliyetleri(-)": {
- "account_type": "Payable"
- },
- "Finansal Kiralama \u0130\u015flemlerinden Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Menkul K\u0131ymetler \u0130hra\u00e7 Fark\u0131(-)": {
- "account_type": "Payable"
- },
- "account_type": "Payable",
- "\u00c7\u0131kar\u0131lm\u0131\u015f Di\u011fer Menkul K\u0131ymetler": {
- "account_type": "Payable"
- },
- "\u00c7\u0131kar\u0131lm\u0131\u015f Tahviller": {
- "account_type": "Payable"
- }
- },
- "Ticari Bor\u00e7lar": {
- "Al\u0131nan Depozito Ve Teminatlar": {
- "account_type": "Payable"
- },
- "Bor\u00e7 Senetleri": {
- "account_type": "Payable"
- },
- "Bor\u00e7 Senetleri Reeskontu(-)": {
- "account_type": "Payable"
- },
- "Di\u011fer Ticari Bor\u00e7lar": {
- "account_type": "Payable"
- },
- "Sat\u0131c\u0131lar": {
- "account_type": "Payable"
- },
- "account_type": "Payable"
- },
- "root_type": ""
- },
- "\u00d6z Kaynaklar": {
- "D\u00f6nem Net K\u00e2r\u0131 (Zarar\u0131)": {
- "D\u00f6nem Net K\u00e2r\u0131": {},
- "D\u00f6nem Net Zarar\u0131(-)": {}
- },
- "Ge\u00e7mi\u015f Y\u0131llar K\u00e2rlar\u0131": {
- "Ge\u00e7mi\u015f Y\u0131llar K\u00e2rlar\u0131": {}
- },
- "Ge\u00e7mi\u015f Y\u0131llar Zararlar\u0131(-)": {
- "Ge\u00e7mi\u015f Y\u0131llar Zararlar\u0131(-)": {}
- },
- "K\u00e2r Yedekleri": {
- "Di\u011fer K\u00e2r Yedekleri": {},
- "Ola\u011fan\u00fcst\u00fc Yedekler": {},
- "Stat\u00fc Yedekleri": {},
- "Yasal Yedekler": {},
- "\u00d6zel Fonlar": {}
- },
- "Sermaye Yedekleri": {
- "Di\u011fer Sermaye Yedekleri": {},
- "Hisse Senedi \u0130ptal K\u00e2rlar\u0131": {},
- "Hisse Senetleri \u0130hra\u00e7 Primleri": {},
- "Maddi Duran Varl\u0131k Yeniden De\u011ferlenme Art\u0131\u015flar\u0131": {},
- "Maliyet Art\u0131\u015flar\u0131 Fonu": {},
- "\u0130\u015ftirakler Yeniden De\u011ferleme Art\u0131\u015flar\u0131": {}
- },
- "root_type": "",
- "\u00d6denmi\u015f Sermaye": {
- "Sermaye": {},
- "\u00d6denmi\u015f Sermaye(-)": {
- "account_type": "Payable"
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/tr_chart_of_accounts.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/tr_chart_of_accounts.json
new file mode 100644
index 00000000000..fa8860883d2
--- /dev/null
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/tr_chart_of_accounts.json
@@ -0,0 +1,1473 @@
+{
+ "country_code": "tr",
+ "name": "Turkey - Chart of Accounts",
+ "tree": {
+ "D\u00d6NEN VARLIKLAR": {
+ "account_number": "1",
+ "root_type": "Asset",
+ "HAZIR DE\u011eERLER": {
+ "account_number": "10",
+ "KASA": {
+ "account_number": "100",
+ "is_group": 1,
+ "account_type": "Cash",
+ "TL KASA": {
+ "account_number": "100.01",
+ "account_type": "Cash"
+ },
+ "EUR KASA": {
+ "account_number": "100.02",
+ "account_type": "Cash"
+ },
+ "USD KASA": {
+ "account_number": "100.03",
+ "account_type": "Cash"
+ }
+ },
+ "ALINAN \u00c7EKLER": {
+ "account_number": "101",
+ "is_group": 1
+ },
+ "BANKALAR": {
+ "account_number": "102",
+ "is_group": 1,
+ "account_type": "Bank"
+ },
+ "VER\u0130LEN \u00c7EKLER VE \u00d6DEME EM\u0130RLER\u0130(-)": {
+ "account_number": "103",
+ "is_group": 1
+ },
+ "D\u0130\u011eER HAZIR DE\u011eERLER": {
+ "account_number": "108",
+ "is_group": 1
+ }
+ },
+ "MENKUL KIYMETLER": {
+ "account_number": "11",
+ "H\u0130SSE SENETLER\u0130": {
+ "account_number": "110",
+ "is_group": 1
+ },
+ "\u00d6ZEL KES\u0130M TAHV\u0130L SENET VE BONOLARI": {
+ "account_number": "111",
+ "is_group": 1
+ },
+ "KAMU KES\u0130M\u0130 TAHV\u0130L, SENET VE BONOLARI": {
+ "account_number": "112",
+ "is_group": 1
+ },
+ "D\u0130\u011eER MENKUL KIYMETLER": {
+ "account_number": "118",
+ "is_group": 1
+ },
+ "MENKUL KIYMETLER DE\u011eER D\u00dc\u015e\u00dcKL\u00dc\u011e\u00dc KAR\u015eILI\u011eI(-)": {
+ "account_number": "119",
+ "is_group": 1
+ }
+ },
+ "T\u0130CAR\u0130 ALACAKLAR": {
+ "account_number": "12",
+ "ALICILAR": {
+ "account_number": "120",
+ "ALICILAR": {
+ "account_number": "120.01",
+ "account_type": "Receivable"
+ }
+ },
+ "ALACAK SENETLER\u0130": {
+ "account_number": "121",
+ "is_group": 1
+ },
+ "ALACAK SENETLER\u0130 REESKONTU(-)": {
+ "account_number": "122",
+ "is_group": 1
+ },
+ "KAZANILMAMI\u015e F\u0130NANSAL K\u0130RALAMA FA\u0130Z GEL\u0130RLER\u0130 (-)": {
+ "account_number": "124",
+ "is_group": 1
+ },
+ "VER\u0130LEN DEPOZ\u0130TO VE TEM\u0130NATLAR": {
+ "account_number": "126",
+ "is_group": 1
+ },
+ "D\u0130\u011eER T\u0130CAR\u0130 ALACAKLAR": {
+ "account_number": "127",
+ "is_group": 1
+ },
+ "\u015e\u00dcPHEL\u0130 T\u0130CAR\u0130 ALACAKLAR": {
+ "account_number": "128",
+ "is_group": 1
+ },
+ "\u015e\u00dcPHEL\u0130 T\u0130CAR\u0130 ALACAKLAR KAR\u015eILI\u011eI(-)": {
+ "account_number": "129",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER ALACAKLAR": {
+ "account_number": "13",
+ "ORTAKLARDAN ALACAKLAR": {
+ "account_number": "131",
+ "is_group": 1
+ },
+ "\u0130\u015eT\u0130RAKLERDEN ALACAKLAR": {
+ "account_number": "132",
+ "is_group": 1
+ },
+ "BA\u011eLI ORTAKLIKLARDAN ALACAKLAR": {
+ "account_number": "133",
+ "is_group": 1
+ },
+ "PERSONELDEN ALACAKLAR": {
+ "account_number": "135",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u00c7E\u015e\u0130TL\u0130 ALACAKLAR": {
+ "account_number": "136",
+ "is_group": 1
+ },
+ "D\u0130\u011eER ALACAK SENETLER\u0130 REESKONTU(-)": {
+ "account_number": "137",
+ "is_group": 1
+ },
+ "\u015e\u00dcPHEL\u0130 D\u0130\u011eER ALACAKLAR": {
+ "account_number": "138",
+ "is_group": 1
+ },
+ "\u015e\u00dcPHEL\u0130 D\u0130\u011eER ALACAKLAR KAR\u015eILI\u011eI(-)": {
+ "account_number": "139",
+ "is_group": 1
+ }
+ },
+ "STOKLAR": {
+ "account_number": "15",
+ "\u0130LK MADDE MALZEME": {
+ "account_number": "150",
+ "is_group": 1
+ },
+ "YARI MAMULLER": {
+ "account_number": "151",
+ "is_group": 1
+ },
+ "MAMULLER": {
+ "account_number": "152",
+ "is_group": 1
+ },
+ "T\u0130CAR\u0130 MALLAR": {
+ "account_number": "153",
+ "is_group": 1
+ },
+ "D\u0130\u011eER STOKLAR": {
+ "account_number": "157",
+ "D\u0130\u011eR. STOK.": {
+ "account_number": "157.01",
+ "account_type": "Stock"
+ }
+ },
+ "STOK DE\u011eER D\u00dc\u015e\u00dcKL\u00dc\u011e\u00dc KAR\u015eILI\u011eI(-)": {
+ "account_number": "158",
+ "is_group": 1
+ },
+ "VER\u0130LEN S\u0130PAR\u0130\u015e AVANSLARI": {
+ "account_number": "159",
+ "is_group": 1
+ }
+ },
+ "YILLARA YAYGIN \u0130N\u015eAAT VE ONARIM MAL\u0130YETLER\u0130": {
+ "account_number": "17",
+ "YILLARA YAYGIN \u0130N\u015eAAT VE ONARIM MAL\u0130YETLER\u0130": {
+ "account_number": "170",
+ "is_group": 1
+ },
+ "(YILLARA YAYGIN \u0130N\u015eAAT VE ONARIM MAL\u0130YETLER\u0130)": {
+ "account_number": "177",
+ "is_group": 1
+ },
+ "(YILLARA YAYGIN \u0130N\u015eAAT ENFLASYON D\u00dcZELTME)": {
+ "account_number": "178",
+ "is_group": 1
+ },
+ "TA\u015eERONLARA VER\u0130LEN AVANSLAR": {
+ "account_number": "179",
+ "is_group": 1
+ }
+ },
+ "GELECEK AYLARA A\u0130T G\u0130DERLER VE GEL\u0130R TAHAKKUKLARI": {
+ "account_number": "18",
+ "GELECEK AYLARA A\u0130T G\u0130DERLER": {
+ "account_number": "180",
+ "is_group": 1
+ },
+ "GEL\u0130R TAHAKKUKLARI": {
+ "account_number": "181",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER D\u00d6NEN VARLIKLAR": {
+ "account_number": "19",
+ "DEVREDEN KDV": {
+ "account_number": "190",
+ "is_group": 1
+ },
+ "\u0130ND\u0130R\u0130LECEK KDV": {
+ "account_number": "191",
+ "\u0130ND\u0130R\u0130LECEK KDV": {
+ "account_number": "191.01",
+ "% 18'L\u0130K \u0130ND\u0130R\u0130LECEK KDV": {
+ "account_number": "191.01.01",
+ "account_type": "Tax"
+ },
+ "% 8'L\u0130K \u0130ND\u0130R\u0130LECEK KDV": {
+ "account_number": "191.01.02",
+ "account_type": "Tax"
+ },
+ "% 1'L\u0130K \u0130ND\u0130R\u0130LECEK KDV": {
+ "account_number": "191.01.03",
+ "account_type": "Tax"
+ },
+ "% 20'L\u0130K \u0130ND\u0130R\u0130LECEK KDV": {
+ "account_number": "191.01.04",
+ "account_type": "Tax"
+ },
+ "% 10'L\u0130K \u0130ND\u0130R\u0130LECEK KDV": {
+ "account_number": "191.01.05",
+ "account_type": "Tax"
+ }
+ }
+ },
+ "D\u0130\u011eER KDV": {
+ "account_number": "192",
+ "is_group": 1
+ },
+ "PE\u015e\u0130N \u00d6DENEN VERG\u0130LER VE FONLAR": {
+ "account_number": "193",
+ "is_group": 1
+ },
+ "\u0130\u015e AVANSLARI": {
+ "account_number": "195",
+ "is_group": 1
+ },
+ "PERSONEL AVANSLARI": {
+ "account_number": "196",
+ "is_group": 1
+ },
+ "SAYIM VE TESELL\u00dcM NOKSANLARI": {
+ "account_number": "197",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u00c7E\u015e\u0130TL\u0130 D\u00d6NEN VARLIKLAR": {
+ "account_number": "198",
+ "is_group": 1
+ },
+ "D\u0130\u011eER D\u00d6NEN VARLIKLAR KAR\u015eILI\u011eI(-)": {
+ "account_number": "199",
+ "is_group": 1
+ }
+ }
+ },
+ "DURAN VARLIKLAR": {
+ "account_number": "2",
+ "root_type": "Asset",
+ "T\u0130CAR\u0130 ALACAKLAR": {
+ "account_number": "22",
+ "ALICILAR": {
+ "account_number": "220",
+ "is_group": 1
+ },
+ "ALACAK SENETLER\u0130": {
+ "account_number": "221",
+ "is_group": 1
+ },
+ "ALACAK SENETLER\u0130 REESKONTU(-)": {
+ "account_number": "222",
+ "is_group": 1
+ },
+ "KAZANILMAMI\u015e F\u0130NANSAL K\u0130RALAMA FA\u0130Z GEL\u0130RLER\u0130 (-)-": {
+ "account_number": "224",
+ "is_group": 1
+ },
+ "VER\u0130LEN DEPOZ\u0130TO VE TEM\u0130NATLAR": {
+ "account_number": "226",
+ "is_group": 1
+ },
+ "\u015e\u00dcPHEL\u0130 T\u0130CAR\u0130 ALACAKLAR KAR\u015eILI\u011eI(-)": {
+ "account_number": "229",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER ALACAKLAR": {
+ "account_number": "23",
+ "ORTAKLARDAN ALACAKLAR": {
+ "account_number": "231",
+ "is_group": 1
+ },
+ "\u0130\u015eT\u0130RAKLERDEN ALACAKLAR": {
+ "account_number": "232",
+ "is_group": 1
+ },
+ "BA\u011eLI ORTAKLIKLARDAN ALACAKLAR": {
+ "account_number": "233",
+ "is_group": 1
+ },
+ "PERSONELDEN ALACAKLAR": {
+ "account_number": "235",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u00c7E\u015e\u0130TL\u0130 ALACAKLAR": {
+ "account_number": "236",
+ "is_group": 1
+ },
+ "D\u0130\u011eER ALACAK SENETLER\u0130 REESKONTU(-)": {
+ "account_number": "237",
+ "is_group": 1
+ },
+ "\u015e\u00dcPHEL\u0130 D\u0130\u011eER ALACAKLAR KAR\u015eILI\u011eI(-)": {
+ "account_number": "239",
+ "is_group": 1
+ }
+ },
+ "MAL\u0130 DURAN VARLIKLAR": {
+ "account_number": "24",
+ "BA\u011eLI MENKUL KIYMETLER": {
+ "account_number": "240",
+ "BA\u011eL. MEKL. KIYM.": {
+ "account_number": "240.01",
+ "account_type": "Fixed Asset"
+ }
+ },
+ "BA\u011eLI MENKUL KIYMETLER DE\u011eER D\u00dc\u015e\u00dcKL\u00dc\u011e\u00dc KAR\u015eILI\u011eI(-)": {
+ "account_number": "241",
+ "is_group": 1
+ },
+ "\u0130\u015eT\u0130RAKLER": {
+ "account_number": "242",
+ "is_group": 1
+ },
+ "\u0130\u015eT\u0130RAKLERE SERMAYE TAAHH\u00dcTLER\u0130(-)": {
+ "account_number": "243",
+ "is_group": 1
+ },
+ "\u0130\u015eT\u0130RAKLER SERMAYE PAYLARI DE\u011eER D\u00dc\u015e\u00dcKL\u00dc\u011e\u00dc KAR\u015eILI\u011eI(-)": {
+ "account_number": "244",
+ "is_group": 1
+ },
+ "BA\u011eLI ORTAKLIKLAR": {
+ "account_number": "245",
+ "is_group": 1
+ },
+ "BA\u011eLI ORTAKLIKLARA SERMAYE TAAHH\u00dcTLER\u0130(-)": {
+ "account_number": "246",
+ "is_group": 1
+ },
+ "BA\u011eLI ORTAKLIKLAR SERMAYE PAYLARI DE\u011eER D\u00dc\u015e\u00dcKL\u00dc\u011e\u00dc KAR\u015eILI\u011eI(-)": {
+ "account_number": "247",
+ "is_group": 1
+ },
+ "D\u0130\u011eER MAL\u0130 DURAN VARLIKLAR": {
+ "account_number": "248",
+ "is_group": 1
+ },
+ "D\u0130\u011eER MAL\u0130 DURAN VARLIKLAR KAR\u015eILI\u011eI(-)": {
+ "account_number": "249",
+ "is_group": 1
+ }
+ },
+ "MADD\u0130 DURAN VARLIKLAR": {
+ "account_number": "25",
+ "ARAZ\u0130 VE ARSALAR": {
+ "account_number": "250",
+ "is_group": 1
+ },
+ "YER ALTI VE YER \u00dcST\u00dc D\u00dcZENLER\u0130": {
+ "account_number": "251",
+ "is_group": 1
+ },
+ "B\u0130NALAR": {
+ "account_number": "252",
+ "is_group": 1
+ },
+ "TES\u0130S, MAK\u0130NE VE C\u0130HAZLAR": {
+ "account_number": "253",
+ "is_group": 1
+ },
+ "TA\u015eITLAR": {
+ "account_number": "254",
+ "is_group": 1
+ },
+ "DEM\u0130RBA\u015eLAR": {
+ "account_number": "255",
+ "is_group": 1
+ },
+ "D\u0130\u011eER MADD\u0130 DURAN VARLIKLAR": {
+ "account_number": "256",
+ "is_group": 1
+ },
+ "B\u0130R\u0130KM\u0130\u015e AMORT\u0130SMANLAR(-)": {
+ "account_number": "257",
+ "is_group": 1
+ },
+ "YAPILMAKTA OLAN YATIRIMLAR": {
+ "account_number": "258",
+ "is_group": 1
+ },
+ "VER\u0130LEN AVANSLAR": {
+ "account_number": "259",
+ "is_group": 1
+ }
+ },
+ "MADD\u0130 OLMAYAN DURAN VARLIKLAR": {
+ "account_number": "26",
+ "HAKLAR": {
+ "account_number": "260",
+ "is_group": 1
+ },
+ "\u015eEREF\u0130YE": {
+ "account_number": "261",
+ "is_group": 1
+ },
+ "KURULU\u015e VE \u00d6RG\u00dcTLENME G\u0130DERLER\u0130": {
+ "account_number": "262",
+ "is_group": 1
+ },
+ "ARA\u015eTIRMA VE GEL\u0130\u015eT\u0130RME G\u0130DERLER\u0130": {
+ "account_number": "263",
+ "is_group": 1
+ },
+ "\u00d6ZEL MAL\u0130YETLER": {
+ "account_number": "264",
+ "\u00d6ZL. MAL\u0130YT.": {
+ "account_number": "264.01",
+ "account_type": "Depreciation"
+ }
+ },
+ "D\u0130\u011eER MADD\u0130 OLMAYAN DURAN VARLIKLAR": {
+ "account_number": "267",
+ "is_group": 1
+ },
+ "B\u0130R\u0130KM\u0130\u015e AMORT\u0130SMANLAR(-)": {
+ "account_number": "268",
+ "is_group": 1
+ },
+ "VER\u0130LEN AVANSLAR": {
+ "account_number": "269",
+ "is_group": 1
+ }
+ },
+ "\u00d6ZEL T\u00dcKENMEYE TAB\u0130 VARLIKLAR": {
+ "account_number": "27",
+ "ARAMA G\u0130DERLER\u0130": {
+ "account_number": "271",
+ "is_group": 1
+ },
+ "HAZIRLIK VE GEL\u0130\u015eT\u0130RME G\u0130DERLER\u0130": {
+ "account_number": "272",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u00d6ZEL T\u00dcKENMEYE TAB\u0130 VARLIKLAR": {
+ "account_number": "277",
+ "is_group": 1
+ },
+ "B\u0130R\u0130KM\u0130\u015e T\u00dcKENME PAYLARI(-)": {
+ "account_number": "278",
+ "is_group": 1
+ },
+ "VER\u0130LEN AVANSLAR": {
+ "account_number": "279",
+ "is_group": 1
+ }
+ },
+ "GELECEK YILLARA A\u0130T G\u0130DERLER VE GEL\u0130R TAHAKKUKLARI": {
+ "account_number": "28",
+ "GELECEK YILLARA A\u0130T G\u0130DERLER": {
+ "account_number": "280",
+ "is_group": 1
+ },
+ "GEL\u0130R TAHAKKUKLARI": {
+ "account_number": "281",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER DURAN VARLIKLAR": {
+ "account_number": "29",
+ "GELECEK YILLARDA \u0130ND\u0130R\u0130LECEK KDV": {
+ "account_number": "291",
+ "is_group": 1
+ },
+ "D\u0130\u011eER KDV": {
+ "account_number": "292",
+ "is_group": 1
+ },
+ "GELECEK YILLAR \u0130HT\u0130YACI STOKLAR": {
+ "account_number": "293",
+ "is_group": 1
+ },
+ "ELDEN \u00c7IKARILACAK STOKLAR VE MADD\u0130 DURAN VARLIKLAR": {
+ "account_number": "294",
+ "is_group": 1
+ },
+ "PE\u015e\u0130N \u00d6DENEN VERG\u0130 VE FONLAR": {
+ "account_number": "295",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u00c7E\u015e\u0130TL\u0130 DURAN VARLIKLAR": {
+ "account_number": "297",
+ "is_group": 1
+ },
+ "STOK DE\u011eER D\u00dc\u015e\u00dcKL\u00dc\u011e\u00dc KAR\u015eILI\u011eI(-)": {
+ "account_number": "298",
+ "is_group": 1
+ },
+ "B\u0130R\u0130KM\u0130\u015e AMORT\u0130SMANLAR(-)": {
+ "account_number": "299",
+ "is_group": 1
+ }
+ }
+ },
+ "KISA VADEL\u0130 YABANCI KAYNAKLAR": {
+ "account_number": "3",
+ "root_type": "Liability",
+ "MAL\u0130 BOR\u00c7LAR": {
+ "account_number": "30",
+ "BANKA KRED\u0130LER\u0130": {
+ "account_number": "300",
+ "is_group": 1
+ },
+ "F\u0130NANSAL K\u0130RALAMA \u0130\u015eLEMLER\u0130NDEN BOR\u00c7LAR": {
+ "account_number": "301",
+ "is_group": 1
+ },
+ "ERTELENM\u0130\u015e F\u0130NANSAL K\u0130RALAMA BOR\u00c7LANMA MAL\u0130YETLER\u0130(-)": {
+ "account_number": "302",
+ "is_group": 1
+ },
+ "UZUN VADEL\u0130 KRED\u0130LER\u0130N ANAPARA TAKS\u0130TLER\u0130 VE FA\u0130ZLER\u0130": {
+ "account_number": "303",
+ "is_group": 1
+ },
+ "TAHV\u0130L ANAPARA BOR\u00c7, TAKS\u0130T VE FA\u0130ZLER\u0130": {
+ "account_number": "304",
+ "is_group": 1
+ },
+ "\u00c7IKARILAN BONOLAR VE SENETLER": {
+ "account_number": "305",
+ "is_group": 1
+ },
+ "\u00c7IKARILMI\u015e D\u0130\u011eER MENKUL KIYMETLER": {
+ "account_number": "306",
+ "is_group": 1
+ },
+ "MENKUL KIYMETLER \u0130HRA\u00c7 FARKI(-)": {
+ "account_number": "308",
+ "is_group": 1
+ },
+ "D\u0130\u011eER MAL\u0130 BOR\u00c7LAR": {
+ "account_number": "309",
+ "is_group": 1
+ }
+ },
+ "T\u0130CAR\u0130 BOR\u00c7LAR": {
+ "account_number": "32",
+ "SATICILAR": {
+ "account_number": "320",
+ "SATICILAR TRY": {
+ "account_number": "320.01"
+ }
+ },
+ "BOR\u00c7 SENETLER\u0130": {
+ "account_number": "321",
+ "is_group": 1
+ },
+ "BOR\u00c7 SENETLER\u0130 REESKONTU(-)": {
+ "account_number": "322",
+ "is_group": 1
+ },
+ "ALINAN DEPOZ\u0130TO VE TEM\u0130NATLAR": {
+ "account_number": "326",
+ "is_group": 1
+ },
+ "D\u0130\u011eER T\u0130CAR\u0130 BOR\u00c7LAR": {
+ "account_number": "329",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER BOR\u00c7LAR": {
+ "account_number": "33",
+ "ORTAKLARA BOR\u00c7LAR": {
+ "account_number": "331",
+ "is_group": 1
+ },
+ "\u0130\u015eT\u0130RAKLERE BOR\u00c7LAR": {
+ "account_number": "332",
+ "is_group": 1
+ },
+ "BA\u011eLI ORTAKLIKLARA BOR\u00c7LAR": {
+ "account_number": "333",
+ "is_group": 1
+ },
+ "PERSONELE BOR\u00c7LAR": {
+ "account_number": "335",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u00c7E\u015e\u0130TL\u0130 BOR\u00c7LAR": {
+ "account_number": "336",
+ "is_group": 1
+ },
+ "D\u0130\u011eER BOR\u00c7 SENETLER\u0130 REESKONTU(-)": {
+ "account_number": "337",
+ "is_group": 1
+ }
+ },
+ "ALINAN AVANSLAR": {
+ "account_number": "34",
+ "ALINAN S\u0130PAR\u0130\u015e AVANSLARI": {
+ "account_number": "340",
+ "is_group": 1
+ },
+ "ALINAN D\u0130\u011eER AVANSLAR": {
+ "account_number": "349",
+ "is_group": 1
+ }
+ },
+ "YILLARA YAYGIN \u0130N\u015eAAT VE ONARIM HAKED\u0130\u015eLER\u0130": {
+ "account_number": "35",
+ "YILLARA YAYGIN \u0130N\u015eAAT VE ONARIM HAKED\u0130\u015e BEDELLER\u0130": {
+ "account_number": "358",
+ "is_group": 1
+ }
+ },
+ "\u00d6DENECEK VERG\u0130 VE D\u0130\u011eER Y\u00dcK\u00dcML\u00dcL\u00dcKLER": {
+ "account_number": "36",
+ "\u00d6DENECEK VERG\u0130 VE FONLAR": {
+ "account_number": "360",
+ "is_group": 1
+ },
+ "\u00d6DENECEK SOSYAL G\u00dcVENL\u0130K KES\u0130NT\u0130LER\u0130": {
+ "account_number": "361",
+ "is_group": 1
+ },
+ "VADES\u0130 GE\u00c7M\u0130\u015e, ERTELENM\u0130\u015e VEYA TAKS\u0130TLEND\u0130R\u0130LM\u0130\u015e VERG\u0130 VE D\u0130\u011eER Y\u00dcK\u00dcML\u00dcL\u00dcKLER": {
+ "account_number": "368",
+ "is_group": 1
+ },
+ "\u00d6DENECEK D\u0130\u011eER Y\u00dcK\u00dcML\u00dcL\u00dcKLER": {
+ "account_number": "369",
+ "is_group": 1
+ }
+ },
+ "BOR\u00c7 VE G\u0130DER KAR\u015eILIKLARI": {
+ "account_number": "37",
+ "D\u00d6NEM K\u00c2RI VERG\u0130 VE D\u0130\u011eER YASAL Y\u00dcK\u00dcML\u00dcL\u00dcK KAR\u015eILIKLARI": {
+ "account_number": "370",
+ "is_group": 1
+ },
+ "D\u00d6NEM K\u00c2RININ PE\u015e\u0130N \u00d6DENEN VERG\u0130 VE D\u0130\u011eER Y\u00dcK\u00dcML\u00dcL\u00dcKLER\u0130(-)": {
+ "account_number": "371",
+ "is_group": 1
+ },
+ "KIDEM TAZM\u0130NATI KAR\u015eILI\u011eI": {
+ "account_number": "372",
+ "is_group": 1
+ },
+ "MAL\u0130YET G\u0130DERLER\u0130 KAR\u015eILI\u011eI": {
+ "account_number": "373",
+ "is_group": 1
+ },
+ "D\u0130\u011eER BOR\u00c7 VE G\u0130DER KAR\u015eILIKLARI": {
+ "account_number": "379",
+ "is_group": 1
+ }
+ },
+ "GELECEK AYLARA A\u0130T GEL\u0130RLER VE G\u0130DER TAHAKKUKLARI": {
+ "account_number": "38",
+ "GELECEK AYLARA A\u0130T GEL\u0130RLER": {
+ "account_number": "380",
+ "is_group": 1
+ },
+ "G\u0130DER TAHAKKUKLARI": {
+ "account_number": "381",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER KISA VADEL\u0130 YABANCI KAYNAKLAR": {
+ "account_number": "39",
+ "HESAPLANAN KDV": {
+ "account_number": "391",
+ "HESAPL. KDV": {
+ "account_number": "391.01",
+ "% 18 HESAPLAN KDV": {
+ "account_number": "391.01.01",
+ "account_type": "Tax"
+ },
+ "% 8 HESAPLANAN KDV": {
+ "account_number": "391.01.02",
+ "account_type": "Tax"
+ },
+ "% 1 HESAPLANAN KDV": {
+ "account_number": "391.01.03",
+ "account_type": "Tax"
+ },
+ "% 20 HESAPLANAN KDV": {
+ "account_number": "391.01.04",
+ "account_type": "Tax"
+ },
+ "% 10 HESAPLANAN KDV": {
+ "account_number": "391.01.05",
+ "account_type": "Tax"
+ }
+ }
+ },
+ "D\u0130\u011eER KDV": {
+ "account_number": "392",
+ "is_group": 1
+ },
+ "MERKEZ VE \u015eUBELER CAR\u0130 HESABI": {
+ "account_number": "393",
+ "is_group": 1
+ },
+ "SAYIM VE TESELL\u00dcM FAZLALARI": {
+ "account_number": "397",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u00c7E\u015e\u0130TL\u0130 YABANCI KAYNAKLAR": {
+ "account_number": "399",
+ "is_group": 1
+ }
+ }
+ },
+ "UZUN VADEL\u0130 YABANCI KAYNAKLAR": {
+ "account_number": "4",
+ "root_type": "Liability",
+ "MAL\u0130 BOR\u00c7LAR": {
+ "account_number": "40",
+ "BANKA KRED\u0130LER\u0130": {
+ "account_number": "400",
+ "is_group": 1
+ },
+ "F\u0130NANSAL K\u0130RALAMA \u0130\u015eLEMLER\u0130NDEN BOR\u00c7LAR-": {
+ "account_number": "401",
+ "is_group": 1
+ },
+ "ERTELENM\u0130\u015e F\u0130NANSAL K\u0130RALAMA BOR\u00c7LANMA MAL\u0130YETLER\u0130 (-)": {
+ "account_number": "402",
+ "is_group": 1
+ },
+ "\u00c7IKARILMI\u015e TAHV\u0130LLER": {
+ "account_number": "405",
+ "is_group": 1
+ },
+ "\u00c7IKARILMI\u015e D\u0130\u011eER MENKUL KIYMETLER": {
+ "account_number": "407",
+ "is_group": 1
+ },
+ "MENKUL KIYMETLER \u0130HRA\u00c7 FARKI(-)": {
+ "account_number": "408",
+ "is_group": 1
+ },
+ "D\u0130\u011eER MAL\u0130 BOR\u00c7LAR": {
+ "account_number": "409",
+ "is_group": 1
+ }
+ },
+ "T\u0130CAR\u0130 BOR\u00c7LAR": {
+ "account_number": "42",
+ "SATICILAR": {
+ "account_number": "420",
+ "is_group": 1
+ },
+ "BOR\u00c7 SENETLER\u0130": {
+ "account_number": "421",
+ "is_group": 1
+ },
+ "BOR\u00c7 SENETLER\u0130 REESKONTU(-)": {
+ "account_number": "422",
+ "is_group": 1
+ },
+ "ALINAN DEPOZ\u0130TO VE TEM\u0130NATLAR": {
+ "account_number": "426",
+ "is_group": 1
+ },
+ "D\u0130\u011eER T\u0130CAR\u0130 BOR\u00c7LAR": {
+ "account_number": "429",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER BOR\u00c7LAR": {
+ "account_number": "43",
+ "ORTAKLARA BOR\u00c7LAR": {
+ "account_number": "431",
+ "is_group": 1
+ },
+ "\u0130\u015eT\u0130RAKLERE BOR\u00c7LAR": {
+ "account_number": "432",
+ "is_group": 1
+ },
+ "BA\u011eLI ORTAKLIKLARA BOR\u00c7LAR": {
+ "account_number": "433",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u00c7E\u015e\u0130TL\u0130 BOR\u00c7LAR": {
+ "account_number": "436",
+ "is_group": 1
+ },
+ "D\u0130\u011eER BOR\u00c7 SENETLER\u0130 REESKONTU(-)": {
+ "account_number": "437",
+ "is_group": 1
+ },
+ "KAMUYA OLAN ERTELENM\u0130\u015e VEYA TAKS\u0130TLEND\u0130R\u0130LM\u0130\u015e BOR\u00c7LAR": {
+ "account_number": "438",
+ "is_group": 1
+ }
+ },
+ "ALINAN AVANSLAR": {
+ "account_number": "44",
+ "ALINAN S\u0130PAR\u0130\u015e AVANSLARI": {
+ "account_number": "440",
+ "is_group": 1
+ },
+ "ALINAN D\u0130\u011eER AVANSLAR": {
+ "account_number": "449",
+ "is_group": 1
+ }
+ },
+ "BOR\u00c7 VE G\u0130DER KAR\u015eILIKLARI": {
+ "account_number": "47",
+ "KIDEM TAZM\u0130NATI KAR\u015eILI\u011eI": {
+ "account_number": "472",
+ "is_group": 1
+ },
+ "D\u0130\u011eER BOR\u00c7 VE G\u0130DER KAR\u015eILIKLARI": {
+ "account_number": "479",
+ "is_group": 1
+ }
+ },
+ "GELECEK YILLARA A\u0130T GEL\u0130RLER VE G\u0130DER TAHAKKUKLARI": {
+ "account_number": "48",
+ "GELECEK YILLARA A\u0130T GEL\u0130RLER": {
+ "account_number": "480",
+ "is_group": 1
+ },
+ "G\u0130DER TAHAKKUKLARI": {
+ "account_number": "481",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER UZUN VADEL\u0130 YABANCI KAYNAKLAR": {
+ "account_number": "49",
+ "GELECEK YILLARA ERTELENM\u0130\u015e VEYA TERK\u0130N ED\u0130LECEK KDV": {
+ "account_number": "492",
+ "is_group": 1
+ },
+ "TES\u0130SE KATILMA PAYLARI": {
+ "account_number": "493",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u00c7E\u015e\u0130TL\u0130 UZUN VADEL\u0130 YABANCI KAYNAKLAR": {
+ "account_number": "499",
+ "is_group": 1
+ }
+ }
+ },
+ "\u00d6Z KAYNAKLAR": {
+ "account_number": "5",
+ "root_type": "Equity",
+ "\u00d6DENM\u0130\u015e SERMAYE": {
+ "account_number": "50",
+ "SERMAYE": {
+ "account_number": "500",
+ "is_group": 1
+ },
+ "\u00d6DENMEM\u0130\u015e SERMAYE(-)": {
+ "account_number": "501",
+ "is_group": 1
+ },
+ "SERMAYE D\u00dcZELTMES\u0130 OLUMLU FARKLARI": {
+ "account_number": "502",
+ "is_group": 1
+ },
+ "SERMAYE D\u00dcZELTMES\u0130 OLUMSUZ FARKLARI (-)": {
+ "account_number": "503",
+ "is_group": 1
+ }
+ },
+ "SERMAYE YEDEKLER\u0130": {
+ "account_number": "52",
+ "H\u0130SSE SENETLER\u0130 \u0130HRA\u00c7 PR\u0130MLER\u0130": {
+ "account_number": "520",
+ "is_group": 1
+ },
+ "H\u0130SSE SENED\u0130 \u0130PTAL K\u00c2RLARI": {
+ "account_number": "521",
+ "is_group": 1
+ },
+ "MADD\u0130 DURAN VARLIK YEN\u0130DEN DE\u011eERLEME ARTI\u015eLARI": {
+ "account_number": "522",
+ "is_group": 1
+ },
+ "\u0130\u015eT\u0130RAKLER YEN\u0130DEN DE\u011eERLEME ARTI\u015eLARI": {
+ "account_number": "523",
+ "is_group": 1
+ },
+ "D\u0130\u011eER SERMAYE YEDEKLER\u0130": {
+ "account_number": "529",
+ "is_group": 1
+ }
+ },
+ "K\u00c2R YEDEKLER\u0130": {
+ "account_number": "54",
+ "YASAL YEDEKLER": {
+ "account_number": "540",
+ "is_group": 1
+ },
+ "STAT\u00dc YEDEKLER\u0130": {
+ "account_number": "541",
+ "is_group": 1
+ },
+ "OLA\u011eAN\u00dcST\u00dc YEDEKLER": {
+ "account_number": "542",
+ "is_group": 1
+ },
+ "D\u0130\u011eER K\u00c2R YEDEKLER\u0130": {
+ "account_number": "548",
+ "is_group": 1
+ },
+ "\u00d6ZEL FONLAR": {
+ "account_number": "549",
+ "is_group": 1
+ }
+ },
+ "GE\u00c7M\u0130\u015e YILLAR K\u00c2RLARI": {
+ "account_number": "57",
+ "GE\u00c7M\u0130\u015e YILLAR K\u00c2RLARI": {
+ "account_number": "570",
+ "is_group": 1
+ }
+ },
+ "GE\u00c7M\u0130\u015e YILLAR ZARARLARI(-)": {
+ "account_number": "58",
+ "GE\u00c7M\u0130\u015e YILLAR ZARARLARI(-)": {
+ "account_number": "580",
+ "is_group": 1
+ }
+ },
+ "D\u00d6NEM NET K\u00c2RI (ZARARI)": {
+ "account_number": "59",
+ "D\u00d6NEM NET K\u00c2RI": {
+ "account_number": "590",
+ "is_group": 1
+ },
+ "D\u00d6NEM NET ZARARI(-)": {
+ "account_number": "591",
+ "is_group": 1
+ }
+ }
+ },
+ "GEL\u0130R TABLOSU HESAPLARI": {
+ "account_number": "6",
+ "root_type": "Income",
+ "BR\u00dcT SATI\u015eLAR": {
+ "account_number": "60",
+ "YURT \u0130\u00c7\u0130 SATI\u015eLAR": {
+ "account_number": "600",
+ "is_group": 1
+ },
+ "YURT DI\u015eI SATI\u015eLAR": {
+ "account_number": "601",
+ "is_group": 1
+ },
+ "D\u0130\u011eER GEL\u0130RLER": {
+ "account_number": "602",
+ "is_group": 1
+ }
+ },
+ "SATI\u015e \u0130ND\u0130R\u0130MLER\u0130(-)": {
+ "account_number": "61",
+ "SATI\u015eTAN \u0130ADELER(-)": {
+ "account_number": "610",
+ "is_group": 1
+ },
+ "SATI\u015e \u0130ND\u0130R\u0130MLER\u0130(-)": {
+ "account_number": "611",
+ "is_group": 1
+ },
+ "D\u0130\u011eER \u0130ND\u0130R\u0130MLER(-)": {
+ "account_number": "612",
+ "is_group": 1
+ }
+ },
+ "SATI\u015eLARIN MAL\u0130YET\u0130(-)": {
+ "account_number": "62",
+ "SATILAN MAMULLER MAL\u0130YET\u0130(-)": {
+ "account_number": "620",
+ "is_group": 1
+ },
+ "SATILAN T\u0130CAR\u0130 MALLAR MAL\u0130YET\u0130(-)": {
+ "account_number": "621",
+ "is_group": 1
+ },
+ "SATILAN H\u0130ZMET MAL\u0130YET\u0130(-)": {
+ "account_number": "622",
+ "is_group": 1
+ },
+ "D\u0130\u011eER SATI\u015eLARIN MAL\u0130YET\u0130(-)": {
+ "account_number": "623",
+ "D\u0130\u011eR. SAT\u015e. MALYT.": {
+ "account_number": "623.01"
+ }
+ }
+ },
+ "FAAL\u0130YET G\u0130DERLER\u0130(-)": {
+ "account_number": "63",
+ "ARA\u015eTIRMA VE GEL\u0130\u015eT\u0130RME G\u0130DERLER\u0130(-)": {
+ "account_number": "630",
+ "is_group": 1
+ },
+ "PAZARLAMA SATI\u015e VE DA\u011eITIM G\u0130DERLER\u0130(-)": {
+ "account_number": "631",
+ "is_group": 1
+ },
+ "GENEL Y\u00d6NET\u0130M G\u0130DERLER\u0130(-)": {
+ "account_number": "632",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER FAAL\u0130YETLERDEN OLA\u011eAN GEL\u0130R VE K\u00c2RLAR": {
+ "account_number": "64",
+ "\u0130\u015eT\u0130RAKLERDEN TEMETT\u00dc GEL\u0130RLER\u0130": {
+ "account_number": "640",
+ "is_group": 1
+ },
+ "BA\u011eLI ORTAKLIKLARDAN TEMETT\u00dc GEL\u0130RLER\u0130": {
+ "account_number": "641",
+ "is_group": 1
+ },
+ "FA\u0130Z GEL\u0130RLER\u0130": {
+ "account_number": "642",
+ "is_group": 1
+ },
+ "KOM\u0130SYON GEL\u0130RLER\u0130": {
+ "account_number": "643",
+ "is_group": 1
+ },
+ "KONUSU KALMAYAN KAR\u015eILIKLAR": {
+ "account_number": "644",
+ "is_group": 1
+ },
+ "MENKUL KIYMET SATI\u015e K\u00c2RLARI": {
+ "account_number": "645",
+ "is_group": 1
+ },
+ "KAMB\u0130YO K\u00c2RLARI": {
+ "account_number": "646",
+ "is_group": 1
+ },
+ "REESKONT FA\u0130Z GEL\u0130RLER\u0130": {
+ "account_number": "647",
+ "is_group": 1
+ },
+ "ENFLASYON D\u00dcZELTME K\u00c2RLARI": {
+ "account_number": "648",
+ "is_group": 1
+ },
+ "D\u0130\u011eER OLA\u011eAN GEL\u0130R VE K\u00c2RLAR": {
+ "account_number": "649",
+ "is_group": 1
+ }
+ },
+ "D\u0130\u011eER FAAL\u0130YETLERDEN OLA\u011eAN G\u0130DER VE ZARARLAR(-)": {
+ "account_number": "65",
+ "KOM\u0130SYON G\u0130DERLER\u0130(-)": {
+ "account_number": "653",
+ "is_group": 1
+ },
+ "KAR\u015eILIK G\u0130DERLER\u0130(-)": {
+ "account_number": "654",
+ "is_group": 1
+ },
+ "MENKUL KIYMET SATI\u015e ZARARLARI(-)": {
+ "account_number": "655",
+ "is_group": 1
+ },
+ "KAMB\u0130YO ZARARLARI(-)": {
+ "account_number": "656",
+ "is_group": 1
+ },
+ "REESKONT FA\u0130Z G\u0130DERLER\u0130(-)": {
+ "account_number": "657",
+ "is_group": 1
+ },
+ "ENFLASYON D\u00dcZELTMES\u0130 ZARARLARI(-)": {
+ "account_number": "658",
+ "is_group": 1
+ },
+ "D\u0130\u011eER OLA\u011eAN G\u0130DER VE ZARARLAR(-)": {
+ "account_number": "659",
+ "is_group": 1
+ }
+ },
+ "F\u0130NANSMAN G\u0130DERLER\u0130(-)": {
+ "account_number": "66",
+ "KISA VADEL\u0130 BOR\u00c7LANMA G\u0130DERLER\u0130(-)": {
+ "account_number": "660",
+ "is_group": 1
+ },
+ "UZUN VADEL\u0130 BOR\u00c7LANMA G\u0130DERLER\u0130(-)": {
+ "account_number": "661",
+ "is_group": 1
+ }
+ },
+ "OLA\u011eAN DI\u015eI GEL\u0130R VE K\u00c2RLAR": {
+ "account_number": "67",
+ "\u00d6NCEK\u0130 D\u00d6NEM GEL\u0130R VE K\u00c2RLARI": {
+ "account_number": "671",
+ "is_group": 1
+ },
+ "D\u0130\u011eER OLA\u011eAN DI\u015eI GEL\u0130R VE K\u00c2RLAR": {
+ "account_number": "679",
+ "is_group": 1
+ }
+ },
+ "OLA\u011eAN DI\u015eI G\u0130DER VE ZARARLAR(-)": {
+ "account_number": "68",
+ "\u00c7ALI\u015eMAYAN KISIM G\u0130DER VE ZARARLARI(-)": {
+ "account_number": "680",
+ "is_group": 1
+ },
+ "\u00d6NCEK\u0130 D\u00d6NEM G\u0130DER VE ZARARLARI(-)": {
+ "account_number": "681",
+ "is_group": 1
+ },
+ "D\u0130\u011eER OLA\u011eAN DI\u015eI G\u0130DER VE ZARARLAR(-)": {
+ "account_number": "689",
+ "is_group": 1
+ }
+ },
+ "D\u00d6NEM NET K\u00c2RI VEYA ZARARI": {
+ "account_number": "69",
+ "D\u00d6NEM K\u00c2RI VEYA ZARARI": {
+ "account_number": "690",
+ "is_group": 1
+ },
+ "D\u00d6NEM K\u00c2RI VERG\u0130 VE D\u0130\u011eER YASAL Y\u00dcK\u00dcML\u00dcL\u00dcK KAR\u015eILIKLARI(-)": {
+ "account_number": "691",
+ "is_group": 1
+ },
+ "D\u00d6NEM NET K\u00c2RI VEYA ZARARI": {
+ "account_number": "692",
+ "is_group": 1
+ },
+ "YILLARA YAYGIN \u0130N\u015eAAT VE ENFLASYON D\u00dcZELTME HESABI": {
+ "account_number": "697",
+ "is_group": 1
+ },
+ "ENFLASYON D\u00dcZELTME HESABI": {
+ "account_number": "698",
+ "is_group": 1
+ }
+ }
+ },
+ "MAL\u0130YET HESAPLARI (7/A SE\u00c7ENE\u011e\u0130)": {
+ "account_number": "7",
+ "root_type": "Expense",
+ "MAL\u0130YET MUHASEBES\u0130 BA\u011eLANTI HESAPLARI": {
+ "account_number": "70",
+ "MAL\u0130YET MUHASEBES\u0130 BA\u011eLANTI HESABI": {
+ "account_number": "700",
+ "is_group": 1
+ },
+ "MAL\u0130YET MUHASEBES\u0130 YANSITMA HESABI": {
+ "account_number": "701",
+ "is_group": 1
+ }
+ },
+ "D\u0130REKT \u0130LK MADDE VE MALZEME G\u0130DERLER\u0130": {
+ "account_number": "71",
+ "D\u0130REKT \u0130LK MADDE VE MALZEME G\u0130DERLER\u0130 HESABI": {
+ "account_number": "710",
+ "is_group": 1
+ },
+ "D\u0130REKT \u0130LK MADDE VE MALZEME YANSITMA HESABI": {
+ "account_number": "711",
+ "is_group": 1
+ },
+ "D\u0130REKT \u0130LK MADDE VE MALZEME F\u0130YAT FARKI": {
+ "account_number": "712",
+ "is_group": 1
+ },
+ "D\u0130REKT \u0130LK MADDE VE MALZEME M\u0130KTAR FARKI": {
+ "account_number": "713",
+ "is_group": 1
+ }
+ },
+ "D\u0130REKT \u0130\u015e\u00c7\u0130L\u0130K G\u0130DERLER\u0130": {
+ "account_number": "72",
+ "D\u0130REKT \u0130\u015e\u00c7\u0130L\u0130K G\u0130DERLER\u0130": {
+ "account_number": "720",
+ "is_group": 1
+ },
+ "D\u0130REKT \u0130\u015e\u00c7\u0130L\u0130K G\u0130DERLER\u0130 YANSITMA HESABI": {
+ "account_number": "721",
+ "is_group": 1
+ },
+ "D\u0130REKT \u0130\u015e\u00c7\u0130L\u0130K \u00dcCRET FARKLARI": {
+ "account_number": "722",
+ "is_group": 1
+ },
+ "D\u0130REKT \u0130\u015e\u00c7\u0130L\u0130K S\u00dcRE FARKLARI": {
+ "account_number": "723",
+ "is_group": 1
+ }
+ },
+ "GENEL \u00dcRET\u0130M G\u0130DERLER\u0130": {
+ "account_number": "73",
+ "GENEL \u00dcRET\u0130M G\u0130DERLER\u0130": {
+ "account_number": "730",
+ "is_group": 1
+ },
+ "GENEL \u00dcRET\u0130M G\u0130DERLER\u0130 YANSITMA HESABI": {
+ "account_number": "731",
+ "is_group": 1
+ },
+ "GENEL \u00dcRET\u0130M G\u0130DERLER\u0130 B\u00dcT\u00c7E FARKLARI": {
+ "account_number": "732",
+ "is_group": 1
+ },
+ "GENEL \u00dcRET\u0130M G\u0130DERLER\u0130 VER\u0130ML\u0130L\u0130K G\u0130DERLER\u0130": {
+ "account_number": "733",
+ "is_group": 1
+ },
+ "GENEL \u00dcRET\u0130M G\u0130DERLER\u0130 KAPAS\u0130TE FARKLARI": {
+ "account_number": "734",
+ "is_group": 1
+ }
+ },
+ "H\u0130ZMET \u00dcRET\u0130M MAL\u0130YET\u0130": {
+ "account_number": "74",
+ "H\u0130ZMET \u00dcRET\u0130M MAL\u0130YET\u0130": {
+ "account_number": "740",
+ "is_group": 1
+ },
+ "H\u0130ZMET \u00dcRET\u0130M MAL\u0130YET\u0130 YANSITMA HESABI": {
+ "account_number": "741",
+ "is_group": 1
+ },
+ "H\u0130ZMET \u00dcRET\u0130M MAL\u0130YET\u0130 FARK HESAPLARI": {
+ "account_number": "742",
+ "is_group": 1
+ }
+ },
+ "ARA\u015eTIRMA VE GEL\u0130\u015eT\u0130RME G\u0130DERLER\u0130": {
+ "account_number": "75",
+ "ARA\u015eTIRMA VE GEL\u0130\u015eT\u0130RME G\u0130DERLER\u0130": {
+ "account_number": "750",
+ "is_group": 1
+ },
+ "ARA\u015eTIRMA VE GEL\u0130\u015eT\u0130RME G\u0130DERLER\u0130 YANSITMA HESABI": {
+ "account_number": "751",
+ "is_group": 1
+ },
+ "ARA\u015eTIRMA VE GEL\u0130\u015eT\u0130RME G\u0130DER FARKLARI": {
+ "account_number": "752",
+ "is_group": 1
+ }
+ },
+ "PAZARLAMA SATI\u015e VE DA\u011eITIM G\u0130DERLER\u0130": {
+ "account_number": "76",
+ "PAZARLAMA SATI\u015e VE DA\u011eITIM G\u0130DERLER\u0130": {
+ "account_number": "760",
+ "is_group": 1
+ },
+ "PAZARLAMA SATI\u015e VE DA\u011eITIM G\u0130DERLER\u0130 YANSITMA HESABI": {
+ "account_number": "761",
+ "is_group": 1
+ },
+ "PAZARLAMA SATI\u015e VE DA\u011eITIM G\u0130DERLER\u0130 FARK HESABI": {
+ "account_number": "762",
+ "is_group": 1
+ }
+ },
+ "GENEL Y\u00d6NET\u0130M G\u0130DERLER\u0130": {
+ "account_number": "77",
+ "GENEL Y\u00d6NET\u0130M G\u0130DERLER\u0130": {
+ "account_number": "770",
+ "is_group": 1
+ },
+ "GENEL Y\u00d6NET\u0130M G\u0130DERLER\u0130 YANSITMA HESABI": {
+ "account_number": "771",
+ "is_group": 1
+ },
+ "GENEL Y\u00d6NET\u0130M G\u0130DER FARKLARI HESABI": {
+ "account_number": "772",
+ "is_group": 1
+ }
+ },
+ "F\u0130NANSMAN G\u0130DERLER\u0130": {
+ "account_number": "78",
+ "F\u0130NANSMAN G\u0130DERLER\u0130": {
+ "account_number": "780",
+ "is_group": 1
+ },
+ "F\u0130NANSMAN G\u0130DERLER\u0130 YANSITMA HESABI": {
+ "account_number": "781",
+ "is_group": 1
+ },
+ "F\u0130NANSMAN G\u0130DERLER\u0130 FARK HESABI": {
+ "account_number": "782",
+ "is_group": 1
+ }
+ },
+ "G\u0130DER \u00c7E\u015e\u0130TLER\u0130": {
+ "account_number": "79",
+ "\u0130LK MADDE VE MALZEME G\u0130DERLER\u0130": {
+ "account_number": "790",
+ "is_group": 1
+ },
+ "\u0130\u015e\u00c7\u0130 \u00dcCRET VE G\u0130DERLER\u0130": {
+ "account_number": "791",
+ "is_group": 1
+ },
+ "MEMUR \u00dcCRET VE G\u0130DERLER\u0130": {
+ "account_number": "792",
+ "is_group": 1
+ },
+ "DI\u015eARIDAN SA\u011eLANAN FAYDA VE H\u0130ZMETLER": {
+ "account_number": "793",
+ "is_group": 1
+ },
+ "\u00c7E\u015e\u0130TL\u0130 G\u0130DERLER": {
+ "account_number": "794",
+ "is_group": 1
+ },
+ "VERG\u0130, RES\u0130M VE HAR\u00c7LAR": {
+ "account_number": "795",
+ "is_group": 1
+ },
+ "AMORT\u0130SMAN VE T\u00dcKENME PAYLARI": {
+ "account_number": "796",
+ "is_group": 1
+ },
+ "F\u0130NANSMAN G\u0130DERLER\u0130": {
+ "account_number": "797",
+ "is_group": 1
+ },
+ "G\u0130DER \u00c7E\u015e\u0130TLER\u0130 YANSITMA HESABI": {
+ "account_number": "798",
+ "is_group": 1
+ },
+ "\u00dcRET\u0130M MAL\u0130YET HESABI": {
+ "account_number": "799",
+ "is_group": 1
+ }
+ }
+ },
+ "SERBEST HESAPLAR": {
+ "account_number": "8",
+ "root_type": "Asset",
+ "B\u00dcT\u00c7E GEL\u0130RLER\u0130 HESABI": {
+ "account_number": "800",
+ "is_group": 1
+ },
+ "GEL\u0130R YANSITMA HESABI": {
+ "account_number": "805",
+ "is_group": 1
+ },
+ "B\u00dcT\u00c7E GEL\u0130RLER\u0130NDEN RET VE \u0130ADELER HESABI": {
+ "account_number": "810",
+ "is_group": 1
+ },
+ "B\u00dcT\u00c7E G\u0130DERLER\u0130 HESABI": {
+ "account_number": "830",
+ "is_group": 1
+ },
+ "B\u00dcT\u00c7EDEN MAHSUP ED\u0130LECEK \u00d6DEMELER HESABI": {
+ "account_number": "833",
+ "is_group": 1
+ },
+ "GE\u00c7EN YIL B\u00dcT\u00c7E MAHSUPLARI HESABI": {
+ "account_number": "834",
+ "is_group": 1
+ },
+ "G\u0130DER YANSITMA HESABI": {
+ "account_number": "835",
+ "is_group": 1
+ },
+ "B\u00dcT\u00c7E UYGULAMA SONU\u00c7LARI HESABI": {
+ "account_number": "895",
+ "is_group": 1
+ }
+ },
+ "NAZIM HESAPLAR": {
+ "account_number": "9",
+ "root_type": "Expense",
+ "G\u00d6NDER\u0130LECEK B\u00dcT\u00c7E \u00d6DENEKLER\u0130 HESABI": {
+ "account_number": "900",
+ "is_group": 1
+ },
+ "B\u00dcT\u00c7E \u00d6DENEKLER\u0130 HESABI": {
+ "account_number": "901",
+ "is_group": 1
+ },
+ "B\u00dcT\u00c7E \u00d6DENEK HAREKETLER\u0130 HESABI": {
+ "account_number": "902",
+ "is_group": 1
+ },
+ "KULLANILACAK \u00d6DENEKLER HESABI": {
+ "account_number": "903",
+ "is_group": 1
+ },
+ "\u00d6DENEKLER HESABI": {
+ "account_number": "904",
+ "is_group": 1
+ },
+ "\u00d6DENEKL\u0130 G\u0130DERLER HESABI": {
+ "account_number": "905",
+ "is_group": 1
+ },
+ "MAHSUP D\u00d6NEM\u0130NE AKTARILAN KULLANILACAK \u00d6DENEKLER HESABI": {
+ "account_number": "906",
+ "is_group": 1
+ },
+ "MAHSUP D\u00d6NEM\u0130NE AKTARILAN \u00d6DENEKLER HESABI": {
+ "account_number": "907",
+ "is_group": 1
+ },
+ "TEM\u0130NAT MEKTUPLARI HESABI": {
+ "account_number": "910",
+ "is_group": 1
+ },
+ "TEM\u0130NAT MEKTUPLARI EMANETLER\u0130 HESABI": {
+ "account_number": "911",
+ "is_group": 1
+ },
+ "K\u0130\u015e\u0130LERE A\u0130T MENKUL KIYMETLER HESABI": {
+ "account_number": "912",
+ "is_group": 1
+ },
+ "K\u0130\u015e\u0130LERE A\u0130T MENKUL KIYMET EMANETLER\u0130 HESABI": {
+ "account_number": "913",
+ "is_group": 1
+ },
+ "G\u0130DER TAAHH\u00dcTLER\u0130 HESABI": {
+ "account_number": "920",
+ "is_group": 1
+ },
+ "G\u0130DER TAAHH\u00dcTLER\u0130 KAR\u015eILI\u011eI HESABI": {
+ "account_number": "921",
+ "is_group": 1
+ },
+ "VER\u0130LEN GARANT\u0130LER HESABI": {
+ "account_number": "930",
+ "is_group": 1
+ },
+ "VER\u0130LEN GARANT\u0130LER KAR\u015eILI\u011eI HESABI": {
+ "account_number": "931",
+ "is_group": 1
+ },
+ "STOK AYARLAMA": {
+ "account_number": "940",
+ "STOK SAYIM AYARLAMA": {
+ "account_number": "940.01",
+ "account_type": "Stock Adjustment"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 3ec47ff022a..39f9aaf7a6c 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -194,6 +194,7 @@ class JournalEntry(AccountsController):
self.update_asset_value()
self.update_inter_company_jv()
self.update_invoice_discounting()
+ self.update_booked_depreciation()
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
@@ -225,6 +226,7 @@ class JournalEntry(AccountsController):
self.unlink_inter_company_jv()
self.unlink_asset_adjustment_entry()
self.update_invoice_discounting()
+ self.update_booked_depreciation(1)
def get_title(self):
return self.pay_to_recd_from or self.accounts[0].account
@@ -439,6 +441,25 @@ class JournalEntry(AccountsController):
if status:
inv_disc_doc.set_status(status=status)
+ def update_booked_depreciation(self, cancel=0):
+ for d in self.get("accounts"):
+ if (
+ self.voucher_type == "Depreciation Entry"
+ and d.reference_type == "Asset"
+ and d.reference_name
+ and frappe.get_cached_value("Account", d.account, "root_type") == "Expense"
+ and d.debit
+ ):
+ asset = frappe.get_doc("Asset", d.reference_name)
+ for fb_row in asset.get("finance_books"):
+ if fb_row.finance_book == self.finance_book:
+ if cancel:
+ fb_row.total_number_of_booked_depreciations -= 1
+ else:
+ fb_row.total_number_of_booked_depreciations += 1
+ fb_row.db_update()
+ break
+
def unlink_advance_entry_reference(self):
for d in self.get("accounts"):
if d.is_advance == "Yes" and d.reference_type in ("Sales Invoice", "Purchase Invoice"):
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 69075e5cf6c..a2882206b4a 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -76,7 +76,6 @@ class PaymentEntry(AccountsController):
self.setup_party_account_field()
self.set_missing_values()
self.set_liability_account()
- self.validate_advance_account_currency()
self.set_missing_ref_details(force=True)
self.validate_payment_type()
self.validate_party_details()
@@ -163,22 +162,6 @@ class PaymentEntry(AccountsController):
alert=True,
)
- def validate_advance_account_currency(self):
- if self.book_advance_payments_in_separate_party_account is True:
- company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
- if self.payment_type == "Receive" and self.paid_from_account_currency != company_currency:
- frappe.throw(
- _("Booking advances in foreign currency account: {0} ({1}) is not yet supported.").format(
- frappe.bold(self.paid_from), frappe.bold(self.paid_from_account_currency)
- )
- )
- if self.payment_type == "Pay" and self.paid_to_account_currency != company_currency:
- frappe.throw(
- _("Booking advances in foreign currency account: {0} ({1}) is not yet supported.").format(
- frappe.bold(self.paid_to), frappe.bold(self.paid_to_account_currency)
- )
- )
-
def on_cancel(self):
self.ignore_linked_doctypes = (
"GL Entry",
@@ -1266,7 +1249,7 @@ class PaymentEntry(AccountsController):
dr_or_cr, account = self.get_dr_and_account_for_advances(invoice)
args_dict["account"] = account
- args_dict[dr_or_cr] = invoice.allocated_amount
+ args_dict[dr_or_cr] = self.calculate_base_allocated_amount_for_reference(invoice)
args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount
args_dict.update(
{
@@ -1285,7 +1268,7 @@ class PaymentEntry(AccountsController):
args_dict[dr_or_cr + "_in_account_currency"] = 0
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
args_dict["account"] = self.party_account
- args_dict[dr_or_cr] = invoice.allocated_amount
+ args_dict[dr_or_cr] = self.calculate_base_allocated_amount_for_reference(invoice)
args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount
args_dict.update(
{
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 7f9c55ff24f..72961a6b6ec 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -1237,6 +1237,68 @@ class TestPricingRule(unittest.TestCase):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
+ def test_pricing_rules_with_and_without_apply_multiple(self):
+ item = make_item("PR Item 99")
+
+ test_records = [
+ {
+ "doctype": "Pricing Rule",
+ "title": "_Test discount on item group",
+ "name": "_Test discount on item group",
+ "apply_on": "Item Group",
+ "item_groups": [
+ {
+ "item_group": "Products",
+ }
+ ],
+ "selling": 1,
+ "price_or_product_discount": "Price",
+ "rate_or_discount": "Discount Percentage",
+ "discount_percentage": 60,
+ "has_priority": 1,
+ "company": "_Test Company",
+ "apply_multiple_pricing_rules": True,
+ },
+ {
+ "doctype": "Pricing Rule",
+ "title": "_Test fixed rate on item code",
+ "name": "_Test fixed rate on item code",
+ "apply_on": "Item Code",
+ "items": [
+ {
+ "item_code": item.name,
+ }
+ ],
+ "selling": 1,
+ "price_or_product_discount": "Price",
+ "rate_or_discount": "Rate",
+ "rate": 25,
+ "has_priority": 1,
+ "company": "_Test Company",
+ "apply_multiple_pricing_rules": False,
+ },
+ ]
+
+ for item_group_priority, item_code_priority in [(2, 4), (4, 2)]:
+ item_group_rule = frappe.get_doc(test_records[0].copy())
+ item_group_rule.priority = item_group_priority
+ item_group_rule.insert()
+
+ item_code_rule = frappe.get_doc(test_records[1].copy())
+ item_code_rule.priority = item_code_priority
+ item_code_rule.insert()
+
+ si = create_sales_invoice(qty=5, customer="_Test Customer 1", item=item.name, do_not_submit=True)
+ si.save()
+ self.assertEqual(len(si.pricing_rules), 1)
+ # Item Code rule should've applied as it has higher priority
+ expected_rule = item_group_rule if item_group_priority > item_code_priority else item_code_rule
+ self.assertEqual(si.pricing_rules[0].pricing_rule, expected_rule.name)
+
+ si.delete()
+ item_group_rule.delete()
+ item_code_rule.delete()
+
test_dependencies = ["Campaign"]
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 60c9e26aabe..9c7911d7cae 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -174,12 +174,9 @@ def _get_pricing_rules(apply_on, args, values):
def apply_multiple_pricing_rules(pricing_rules):
- apply_multiple_rule = [
- d.apply_multiple_pricing_rules for d in pricing_rules if d.apply_multiple_pricing_rules
- ]
-
- if not apply_multiple_rule:
- return False
+ for d in pricing_rules:
+ if not d.apply_multiple_pricing_rules:
+ return False
return True
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index fba60cef632..dce742d1021 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -593,7 +593,7 @@ class PurchaseInvoice(BuyingController):
for item in self.get("items"):
validate_account_head(item.idx, item.expense_account, self.company, "Expense")
- def set_against_expense_account(self):
+ def set_against_expense_account(self, force=False):
against_accounts = []
for item in self.get("items"):
if item.expense_account and (item.expense_account not in against_accounts):
@@ -601,6 +601,10 @@ class PurchaseInvoice(BuyingController):
self.against_expense_account = ",".join(against_accounts)
+ def force_set_against_expense_account(self):
+ self.set_against_expense_account()
+ frappe.db.set_value(self.doctype, self.name, "against_expense_account", self.against_expense_account)
+
def po_required(self):
if frappe.db.get_value("Buying Settings", None, "po_required") == "Yes":
if frappe.get_value(
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py
index 6bc19222c98..8c8ba633df0 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py
+++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py
@@ -167,6 +167,10 @@ def start_repost(account_repost_doc=str) -> None:
doc.make_gl_entries_on_cancel()
doc.docstatus = 1
+ if doc.doctype == "Sales Invoice":
+ doc.force_set_against_income_account()
+ else:
+ doc.force_set_against_expense_account()
doc.make_gl_entries()
elif doc.doctype in ["Payment Entry", "Journal Entry", "Expense Claim"]:
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index cb0803db932..babcb417f23 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -943,6 +943,10 @@ class SalesInvoice(SellingController):
against_acc.append(d.income_account)
self.against_income_account = ",".join(against_acc)
+ def force_set_against_income_account(self):
+ self.set_against_income_account()
+ frappe.db.set_value(self.doctype, self.name, "against_income_account", self.against_income_account)
+
def add_remarks(self):
if not self.remarks:
if self.po_no and self.po_date:
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index cb4f0200478..af3916ae469 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -476,7 +476,7 @@ class TestSubscription(FrappeTestCase):
start_date="2021-01-01",
submit_invoice=0,
generate_new_invoices_past_due_date=1,
- party="_Test Subscription Customer",
+ party="_Test Subscription Customer John Doe",
)
# create invoices for the first two moths
@@ -565,10 +565,16 @@ def create_parties():
if not frappe.db.exists("Customer", "_Test Subscription Customer"):
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test Subscription Customer"
- customer.billing_currency = "USD"
+ customer.default_currency = "USD"
customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable USD - _TC"})
customer.insert()
+ if not frappe.db.exists("Customer", "_Test Subscription Customer John Doe"):
+ customer = frappe.new_doc("Customer")
+ customer.customer_name = "_Test Subscription Customer John Doe"
+ customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable - _TC"})
+ customer.insert()
+
def reset_settings():
settings = frappe.get_single("Subscription Settings")
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js
index fad6586466e..ad194ee90a2 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.js
+++ b/erpnext/accounts/report/gross_profit/gross_profit.js
@@ -36,7 +36,7 @@ frappe.query_reports["Gross Profit"] = {
label: __("Group By"),
fieldtype: "Select",
options:
- "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject\nMonthly\nPayment Term",
+ "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject\nCost Center\nMonthly\nPayment Term",
default: "Invoice",
},
{
@@ -63,6 +63,26 @@ frappe.query_reports["Gross Profit"] = {
};
},
},
+ {
+ fieldname: "cost_center",
+ label: __("Cost Center"),
+ fieldtype: "MultiSelectList",
+ get_data: function (txt) {
+ return frappe.db.get_link_options("Cost Center", txt, {
+ company: frappe.query_report.get_filter_value("company"),
+ });
+ },
+ },
+ {
+ fieldname: "project",
+ label: __("Project"),
+ fieldtype: "MultiSelectList",
+ get_data: function (txt) {
+ return frappe.db.get_link_options("Project", txt, {
+ company: frappe.query_report.get_filter_value("company"),
+ });
+ },
+ },
],
tree: true,
name_field: "parent",
@@ -85,3 +105,5 @@ frappe.query_reports["Gross Profit"] = {
return value;
},
};
+
+erpnext.utils.add_dimensions("Gross Profit", 15);
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index c8c8dd9b494..fe2746660eb 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -8,6 +8,11 @@ from frappe import _, qb, scrub
from frappe.query_builder import Order
from frappe.utils import cint, flt, formatdate
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+ get_dimension_with_children,
+)
+from erpnext.accounts.report.financial_statements import get_cost_centers_with_children
from erpnext.controllers.queries import get_match_cond
from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
from erpnext.stock.utils import get_incoming_rate
@@ -120,6 +125,13 @@ def execute(filters=None):
"gross_profit_percent",
],
"project": ["project", "base_amount", "buying_amount", "gross_profit", "gross_profit_percent"],
+ "cost_center": [
+ "cost_center",
+ "base_amount",
+ "buying_amount",
+ "gross_profit",
+ "gross_profit_percent",
+ ],
"territory": [
"territory",
"base_amount",
@@ -299,7 +311,14 @@ def get_columns(group_wise_columns, filters):
"fieldname": "project",
"fieldtype": "Link",
"options": "Project",
- "width": 100,
+ "width": 140,
+ },
+ "cost_center": {
+ "label": _("Cost Center"),
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "options": "Cost Center",
+ "width": 140,
},
"sales_person": {
"label": _("Sales Person"),
@@ -787,6 +806,31 @@ class GrossProfitGenerator:
if self.filters.get("item_code"):
conditions += " and `tabSales Invoice Item`.item_code = %(item_code)s"
+ if self.filters.get("cost_center"):
+ self.filters.cost_center = frappe.parse_json(self.filters.get("cost_center"))
+ self.filters.cost_center = get_cost_centers_with_children(self.filters.cost_center)
+ conditions += " and `tabSales Invoice Item`.cost_center in %(cost_center)s"
+
+ if self.filters.get("project"):
+ self.filters.project = frappe.parse_json(self.filters.get("project"))
+ conditions += " and `tabSales Invoice Item`.project in %(project)s"
+
+ accounting_dimensions = get_accounting_dimensions(as_list=False)
+ if accounting_dimensions:
+ for dimension in accounting_dimensions:
+ if self.filters.get(dimension.fieldname):
+ if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
+ self.filters[dimension.fieldname] = get_dimension_with_children(
+ dimension.document_type, self.filters.get(dimension.fieldname)
+ )
+ conditions += (
+ f" and `tabSales Invoice Item`.{dimension.fieldname} in %({dimension.fieldname})s"
+ )
+ else:
+ conditions += (
+ f" and `tabSales Invoice Item`.{dimension.fieldname} in %({dimension.fieldname})s"
+ )
+
if self.filters.get("warehouse"):
warehouse_details = frappe.db.get_value(
"Warehouse", self.filters.get("warehouse"), ["lft", "rgt"], as_dict=1
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 3a2a942bdf2..99a430cbb40 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -45,7 +45,7 @@
"calculate_depreciation",
"column_break_33",
"opening_accumulated_depreciation",
- "number_of_depreciations_booked",
+ "opening_number_of_booked_depreciations",
"is_fully_depreciated",
"section_break_36",
"finance_books",
@@ -257,12 +257,6 @@
"label": "Opening Accumulated Depreciation",
"options": "Company:company:default_currency"
},
- {
- "depends_on": "eval:(doc.is_existing_asset)",
- "fieldname": "number_of_depreciations_booked",
- "fieldtype": "Int",
- "label": "Number of Depreciations Booked"
- },
{
"collapsible": 1,
"collapsible_depends_on": "eval:doc.calculate_depreciation || doc.is_existing_asset",
@@ -546,6 +540,12 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "depends_on": "eval:(doc.is_existing_asset)",
+ "fieldname": "opening_number_of_booked_depreciations",
+ "fieldtype": "Int",
+ "label": "Opening Number of Booked Depreciations"
}
],
"idx": 72,
@@ -589,7 +589,7 @@
"link_fieldname": "target_asset"
}
],
- "modified": "2024-04-18 16:45:47.306032",
+ "modified": "2024-05-21 13:46:21.066483",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 063a5447ab5..8641bb33fad 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -89,8 +89,8 @@ class Asset(AccountsController):
maintenance_required: DF.Check
naming_series: DF.Literal["ACC-ASS-.YYYY.-"]
next_depreciation_date: DF.Date | None
- number_of_depreciations_booked: DF.Int
opening_accumulated_depreciation: DF.Currency
+ opening_number_of_booked_depreciations: DF.Int
policy_number: DF.Data | None
purchase_amount: DF.Currency
purchase_date: DF.Date | None
@@ -145,7 +145,7 @@ class Asset(AccountsController):
"Asset Depreciation Schedules created:
{0}
Please check, edit if needed, and submit the Asset."
).format(asset_depr_schedules_links)
)
-
+ self.set_total_booked_depreciations()
self.total_asset_cost = self.gross_purchase_amount
self.status = self.get_status()
@@ -417,7 +417,7 @@ class Asset(AccountsController):
if not self.is_existing_asset:
self.opening_accumulated_depreciation = 0
- self.number_of_depreciations_booked = 0
+ self.opening_number_of_booked_depreciations = 0
else:
depreciable_amount = flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
if flt(self.opening_accumulated_depreciation) > depreciable_amount:
@@ -428,15 +428,15 @@ class Asset(AccountsController):
)
if self.opening_accumulated_depreciation:
- if not self.number_of_depreciations_booked:
- frappe.throw(_("Please set Number of Depreciations Booked"))
+ if not self.opening_number_of_booked_depreciations:
+ frappe.throw(_("Please set Opening Number of Booked Depreciations"))
else:
- self.number_of_depreciations_booked = 0
+ self.opening_number_of_booked_depreciations = 0
- if flt(row.total_number_of_depreciations) <= cint(self.number_of_depreciations_booked):
+ if flt(row.total_number_of_depreciations) <= cint(self.opening_number_of_booked_depreciations):
frappe.throw(
_(
- "Row {0}: Total Number of Depreciations cannot be less than or equal to Number of Depreciations Booked"
+ "Row {0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations"
).format(row.idx),
title=_("Invalid Schedule"),
)
@@ -457,6 +457,17 @@ class Asset(AccountsController):
).format(row.idx)
)
+ def set_total_booked_depreciations(self):
+ # set value of total number of booked depreciations field
+ for fb_row in self.get("finance_books"):
+ total_number_of_booked_depreciations = self.opening_number_of_booked_depreciations
+ depr_schedule = get_depr_schedule(self.name, "Active", fb_row.finance_book)
+ if depr_schedule:
+ for je in depr_schedule:
+ if je.journal_entry:
+ total_number_of_booked_depreciations += 1
+ fb_row.db_set("total_number_of_booked_depreciations", total_number_of_booked_depreciations)
+
def validate_expected_value_after_useful_life(self):
for row in self.get("finance_books"):
depr_schedule = get_depr_schedule(self.name, "Draft", row.finance_book)
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index 2c54f55f25c..cc8defc5fe6 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -323,6 +323,7 @@ def _make_journal_entry_for_depreciation(
if not je.meta.get_workflow():
je.submit()
+ asset.reload()
idx = cint(asset_depr_schedule_doc.finance_book_id)
row = asset.get("finance_books")[idx - 1]
row.value_after_depreciation -= depr_schedule.depreciation_amount
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 7e0c3ad6888..742cc3eaa49 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -355,7 +355,7 @@ class TestAsset(AssetSetup):
purchase_date="2020-04-01",
expected_value_after_useful_life=0,
total_number_of_depreciations=5,
- number_of_depreciations_booked=2,
+ opening_number_of_booked_depreciations=2,
frequency_of_depreciation=12,
depreciation_start_date="2023-03-31",
opening_accumulated_depreciation=24000,
@@ -453,7 +453,7 @@ class TestAsset(AssetSetup):
purchase_date="2020-01-01",
expected_value_after_useful_life=0,
total_number_of_depreciations=6,
- number_of_depreciations_booked=1,
+ opening_number_of_booked_depreciations=1,
frequency_of_depreciation=10,
depreciation_start_date="2021-01-01",
opening_accumulated_depreciation=20000,
@@ -739,7 +739,7 @@ class TestDepreciationMethods(AssetSetup):
calculate_depreciation=1,
available_for_use_date="2030-06-06",
is_existing_asset=1,
- number_of_depreciations_booked=2,
+ opening_number_of_booked_depreciations=2,
opening_accumulated_depreciation=47095.89,
expected_value_after_useful_life=10000,
depreciation_start_date="2032-12-31",
@@ -789,7 +789,7 @@ class TestDepreciationMethods(AssetSetup):
available_for_use_date="2030-01-01",
is_existing_asset=1,
depreciation_method="Double Declining Balance",
- number_of_depreciations_booked=1,
+ opening_number_of_booked_depreciations=1,
opening_accumulated_depreciation=50000,
expected_value_after_useful_life=10000,
depreciation_start_date="2031-12-31",
@@ -1123,8 +1123,8 @@ class TestDepreciationBasics(AssetSetup):
self.assertRaises(frappe.ValidationError, asset.save)
- def test_number_of_depreciations_booked(self):
- """Tests if an error is raised when number_of_depreciations_booked is not specified when opening_accumulated_depreciation is."""
+ def test_opening_booked_depreciations(self):
+ """Tests if an error is raised when opening_number_of_booked_depreciations is not specified when opening_accumulated_depreciation is."""
asset = create_asset(
item_code="Macbook Pro",
@@ -1140,9 +1140,9 @@ class TestDepreciationBasics(AssetSetup):
self.assertRaises(frappe.ValidationError, asset.save)
def test_number_of_depreciations(self):
- """Tests if an error is raised when number_of_depreciations_booked >= total_number_of_depreciations."""
+ """Tests if an error is raised when opening_number_of_booked_depreciations >= total_number_of_depreciations."""
- # number_of_depreciations_booked > total_number_of_depreciations
+ # opening_number_of_booked_depreciations > total_number_of_depreciations
asset = create_asset(
item_code="Macbook Pro",
calculate_depreciation=1,
@@ -1151,13 +1151,13 @@ class TestDepreciationBasics(AssetSetup):
expected_value_after_useful_life=10000,
depreciation_start_date="2020-07-01",
opening_accumulated_depreciation=10000,
- number_of_depreciations_booked=5,
+ opening_number_of_booked_depreciations=5,
do_not_save=1,
)
self.assertRaises(frappe.ValidationError, asset.save)
- # number_of_depreciations_booked = total_number_of_depreciations
+ # opening_number_of_booked_depreciations = total_number_of_depreciations
asset_2 = create_asset(
item_code="Macbook Pro",
calculate_depreciation=1,
@@ -1166,7 +1166,7 @@ class TestDepreciationBasics(AssetSetup):
expected_value_after_useful_life=10000,
depreciation_start_date="2020-07-01",
opening_accumulated_depreciation=10000,
- number_of_depreciations_booked=5,
+ opening_number_of_booked_depreciations=5,
do_not_save=1,
)
@@ -1501,19 +1501,17 @@ class TestDepreciationBasics(AssetSetup):
"""
asset = create_asset(calculate_depreciation=1)
- asset.opening_accumulated_depreciation = 2000
- asset.number_of_depreciations_booked = 1
asset.finance_books[0].expected_value_after_useful_life = 100
asset.save()
asset.reload()
- self.assertEqual(asset.finance_books[0].value_after_depreciation, 98000.0)
+ self.assertEqual(asset.finance_books[0].value_after_depreciation, 100000.0)
# changing expected_value_after_useful_life shouldn't affect value_after_depreciation
asset.finance_books[0].expected_value_after_useful_life = 200
asset.save()
asset.reload()
- self.assertEqual(asset.finance_books[0].value_after_depreciation, 98000.0)
+ self.assertEqual(asset.finance_books[0].value_after_depreciation, 100000.0)
def test_asset_cost_center(self):
asset = create_asset(is_existing_asset=1, do_not_save=1)
@@ -1696,7 +1694,7 @@ def create_asset(**args):
"purchase_date": args.purchase_date or "2015-01-01",
"calculate_depreciation": args.calculate_depreciation or 0,
"opening_accumulated_depreciation": args.opening_accumulated_depreciation or 0,
- "number_of_depreciations_booked": args.number_of_depreciations_booked or 0,
+ "opening_number_of_booked_depreciations": args.opening_number_of_booked_depreciations or 0,
"gross_purchase_amount": args.gross_purchase_amount or 100000,
"purchase_amount": args.purchase_amount or 100000,
"maintenance_required": args.maintenance_required or 0,
diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
index c838f8b0f26..da05e930eab 100644
--- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
+++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
@@ -108,7 +108,6 @@
"depends_on": "eval:doc.use_serial_batch_fields === 1",
"fieldname": "serial_no",
"fieldtype": "Text",
- "hidden": 1,
"label": "Serial No",
"print_hide": 1
},
@@ -178,7 +177,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2024-03-05 11:22:57.346889",
+ "modified": "2024-06-26 17:06:22.564438",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Capitalization Stock Item",
@@ -188,4 +187,4 @@
"sort_order": "DESC",
"states": [],
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
index 73838163d3a..36ee7402576 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
@@ -13,7 +13,7 @@
"column_break_2",
"gross_purchase_amount",
"opening_accumulated_depreciation",
- "number_of_depreciations_booked",
+ "opening_number_of_booked_depreciations",
"finance_book",
"finance_book_id",
"depreciation_details_section",
@@ -171,10 +171,10 @@
"read_only": 1
},
{
- "fieldname": "number_of_depreciations_booked",
+ "fieldname": "opening_number_of_booked_depreciations",
"fieldtype": "Int",
"hidden": 1,
- "label": "Number of Depreciations Booked",
+ "label": "Opening Number of Booked Depreciations",
"print_hide": 1,
"read_only": 1
},
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
index f64e9123dc0..bd67a173343 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
@@ -50,7 +50,7 @@ class AssetDepreciationSchedule(Document):
gross_purchase_amount: DF.Currency
naming_series: DF.Literal["ACC-ADS-.YYYY.-"]
notes: DF.SmallText | None
- number_of_depreciations_booked: DF.Int
+ opening_number_of_booked_depreciations: DF.Int
opening_accumulated_depreciation: DF.Currency
rate_of_depreciation: DF.Percent
shift_based: DF.Check
@@ -161,7 +161,7 @@ class AssetDepreciationSchedule(Document):
return (
asset_doc.gross_purchase_amount != self.gross_purchase_amount
or asset_doc.opening_accumulated_depreciation != self.opening_accumulated_depreciation
- or asset_doc.number_of_depreciations_booked != self.number_of_depreciations_booked
+ or asset_doc.opening_number_of_booked_depreciations != self.opening_number_of_booked_depreciations
)
def not_manual_depr_or_have_manual_depr_details_been_modified(self, row):
@@ -194,7 +194,7 @@ class AssetDepreciationSchedule(Document):
self.finance_book = row.finance_book
self.finance_book_id = row.idx
self.opening_accumulated_depreciation = asset_doc.opening_accumulated_depreciation or 0
- self.number_of_depreciations_booked = asset_doc.number_of_depreciations_booked or 0
+ self.opening_number_of_booked_depreciations = asset_doc.opening_number_of_booked_depreciations or 0
self.gross_purchase_amount = asset_doc.gross_purchase_amount
self.depreciation_method = row.depreciation_method
self.total_number_of_depreciations = row.total_number_of_depreciations
@@ -263,7 +263,7 @@ class AssetDepreciationSchedule(Document):
row.db_update()
final_number_of_depreciations = cint(row.total_number_of_depreciations) - cint(
- self.number_of_depreciations_booked
+ self.opening_number_of_booked_depreciations
)
has_pro_rata = _check_is_pro_rata(asset_doc, row)
@@ -328,7 +328,7 @@ class AssetDepreciationSchedule(Document):
if date_of_disposal and getdate(schedule_date) >= getdate(date_of_disposal):
from_date = add_months(
getdate(asset_doc.available_for_use_date),
- (asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation),
+ (asset_doc.opening_number_of_booked_depreciations * row.frequency_of_depreciation),
)
if self.depreciation_schedule:
from_date = self.depreciation_schedule[-1].schedule_date
@@ -378,13 +378,16 @@ class AssetDepreciationSchedule(Document):
from_date = get_last_day(
add_months(
getdate(asset_doc.available_for_use_date),
- ((self.number_of_depreciations_booked - 1) * row.frequency_of_depreciation),
+ (
+ (self.opening_number_of_booked_depreciations - 1)
+ * row.frequency_of_depreciation
+ ),
)
)
else:
from_date = add_months(
getdate(add_days(asset_doc.available_for_use_date, -1)),
- (self.number_of_depreciations_booked * row.frequency_of_depreciation),
+ (self.opening_number_of_booked_depreciations * row.frequency_of_depreciation),
)
depreciation_amount, days, months = _get_pro_rata_amt(
row,
@@ -400,7 +403,8 @@ class AssetDepreciationSchedule(Document):
# In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission
asset_doc.to_date = add_months(
asset_doc.available_for_use_date,
- (n + self.number_of_depreciations_booked) * cint(row.frequency_of_depreciation),
+ (n + self.opening_number_of_booked_depreciations)
+ * cint(row.frequency_of_depreciation),
)
depreciation_amount_without_pro_rata = depreciation_amount
@@ -546,34 +550,47 @@ def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False):
has_pro_rata = False
# if not existing asset, from_date = available_for_use_date
- # otherwise, if number_of_depreciations_booked = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12
+ # otherwise, if opening_number_of_booked_depreciations = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12
# from_date = 01/01/2022
- from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly)
+ from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False)
days = date_diff(row.depreciation_start_date, from_date) + 1
-
- if wdv_or_dd_non_yearly:
- total_days = get_total_days(row.depreciation_start_date, 12)
- else:
- # if frequency_of_depreciation is 12 months, total_days = 365
- total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
-
+ total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
+ if days <= 0:
+ frappe.throw(
+ _(
+ """Error: This asset already has {0} depreciation periods booked.
+ The `depreciation start` date must be at least {1} periods after the `available for use` date.
+ Please correct the dates accordingly."""
+ ).format(
+ asset_doc.opening_number_of_booked_depreciations,
+ asset_doc.opening_number_of_booked_depreciations,
+ )
+ )
if days < total_days:
has_pro_rata = True
-
return has_pro_rata
def _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False):
- if wdv_or_dd_non_yearly:
- return add_months(
+ """
+ if Asset has opening booked depreciations = 9,
+ available for use date = 17-07-2023,
+ depreciation start date = 30-04-2024
+ then from date should be 01-04-2024
+ """
+ if asset_doc.opening_number_of_booked_depreciations > 0:
+ from_date = add_months(
asset_doc.available_for_use_date,
- (asset_doc.number_of_depreciations_booked * 12),
+ (asset_doc.opening_number_of_booked_depreciations * row.frequency_of_depreciation) - 1,
)
+ if is_last_day_of_the_month(row.depreciation_start_date):
+ return add_days(get_last_day(from_date), 1)
+
+ # get from date when depreciation start date is not last day of the month
+ months_difference = month_diff(row.depreciation_start_date, from_date) - 1
+ return add_days(add_months(row.depreciation_start_date, -1 * months_difference), 1)
else:
- return add_months(
- asset_doc.available_for_use_date,
- (asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation),
- )
+ return asset_doc.available_for_use_date
def _get_pro_rata_amt(
@@ -678,7 +695,7 @@ def get_straight_line_or_manual_depr_amount(
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(row.expected_value_after_useful_life)
- ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked)
+ ) / flt(row.total_number_of_depreciations - asset.opening_number_of_booked_depreciations)
def get_daily_prorata_based_straight_line_depr(
@@ -704,7 +721,7 @@ def get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx):
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(row.expected_value_after_useful_life)
- ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked)
+ ) / flt(row.total_number_of_depreciations - asset.opening_number_of_booked_depreciations)
asset_shift_factors_map = get_asset_shift_factors_map()
shift = (
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py
index 6e4966ac6cf..c359715571e 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py
@@ -3,8 +3,11 @@
import frappe
from frappe.tests.utils import FrappeTestCase
-from frappe.utils import cstr
+from frappe.utils import cstr, flt
+from erpnext.assets.doctype.asset.depreciation import (
+ post_depreciation_entries,
+)
from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
get_asset_depr_schedule_doc,
@@ -28,7 +31,7 @@ class TestAssetDepreciationSchedule(FrappeTestCase):
self.assertRaises(frappe.ValidationError, second_asset_depr_schedule.insert)
- def test_daily_prorata_based_depr_on_sl_methond(self):
+ def test_daily_prorata_based_depr_on_sl_method(self):
asset = create_asset(
calculate_depreciation=1,
depreciation_method="Straight Line",
@@ -160,3 +163,69 @@ class TestAssetDepreciationSchedule(FrappeTestCase):
for d in get_depr_schedule(asset.name, "Draft")
]
self.assertEqual(schedules, expected_schedules)
+
+ def test_update_total_number_of_booked_depreciations(self):
+ # check if updates total number of booked depreciations when depreciation gets booked
+ asset = create_asset(
+ item_code="Macbook Pro",
+ calculate_depreciation=1,
+ opening_accumulated_depreciation=2000,
+ opening_number_of_booked_depreciations=2,
+ depreciation_method="Straight Line",
+ available_for_use_date="2020-01-01",
+ depreciation_start_date="2020-03-31",
+ frequency_of_depreciation=1,
+ total_number_of_depreciations=24,
+ submit=1,
+ )
+
+ post_depreciation_entries(date="2021-03-31")
+ asset.reload()
+ """
+ opening_number_of_booked_depreciations = 2
+ number_of_booked_depreciations till 2021-03-31 = 13
+ total_number_of_booked_depreciations = 15
+ """
+ self.assertEqual(asset.finance_books[0].total_number_of_booked_depreciations, 15)
+
+ # cancel depreciation entry
+ depr_entry = get_depr_schedule(asset.name, "Active")[0].journal_entry
+
+ frappe.get_doc("Journal Entry", depr_entry).cancel()
+ asset.reload()
+
+ self.assertEqual(asset.finance_books[0].total_number_of_booked_depreciations, 14)
+
+ def test_schedule_for_wdv_method_for_existing_asset(self):
+ asset = create_asset(
+ calculate_depreciation=1,
+ depreciation_method="Written Down Value",
+ available_for_use_date="2020-07-17",
+ is_existing_asset=1,
+ opening_number_of_booked_depreciations=2,
+ opening_accumulated_depreciation=11666.67,
+ depreciation_start_date="2021-04-30",
+ total_number_of_depreciations=12,
+ frequency_of_depreciation=3,
+ gross_purchase_amount=50000,
+ rate_of_depreciation=40,
+ )
+
+ self.assertEqual(asset.status, "Draft")
+ expected_schedules = [
+ ["2021-04-30", 3833.33, 15500.0],
+ ["2021-07-31", 3833.33, 19333.33],
+ ["2021-10-31", 3833.33, 23166.66],
+ ["2022-01-31", 3833.33, 26999.99],
+ ["2022-04-30", 2300.0, 29299.99],
+ ["2022-07-31", 2300.0, 31599.99],
+ ["2022-10-31", 2300.0, 33899.99],
+ ["2023-01-31", 2300.0, 36199.99],
+ ["2023-04-30", 1380.0, 37579.99],
+ ["2023-07-31", 12420.01, 50000.0],
+ ]
+ schedules = [
+ [cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
+ for d in get_depr_schedule(asset.name, "Draft")
+ ]
+ self.assertEqual(schedules, expected_schedules)
diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
index ba5b5f87826..c269948b742 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
@@ -8,6 +8,7 @@
"finance_book",
"depreciation_method",
"total_number_of_depreciations",
+ "total_number_of_booked_depreciations",
"daily_prorata_based",
"shift_based",
"column_break_5",
@@ -104,12 +105,19 @@
"fieldname": "shift_based",
"fieldtype": "Check",
"label": "Depreciate based on shifts"
+ },
+ {
+ "default": "0",
+ "fieldname": "total_number_of_booked_depreciations",
+ "fieldtype": "Int",
+ "label": "Total Number of Booked Depreciations ",
+ "read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-12-29 08:49:39.876439",
+ "modified": "2024-05-21 15:48:20.907250",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",
diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py
index f812a0816dd..d06d6355ec3 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py
@@ -28,6 +28,7 @@ class AssetFinanceBook(Document):
rate_of_depreciation: DF.Percent
salvage_value_percentage: DF.Percent
shift_based: DF.Check
+ total_number_of_booked_depreciations: DF.Int
total_number_of_depreciations: DF.Int
value_after_depreciation: DF.Currency
# end: auto-generated types
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index 27542bc6de8..ccde836fe0d 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -377,7 +377,7 @@ class AssetRepair(AccountsController):
def calculate_last_schedule_date(self, asset, row, extra_months):
asset.flags.increase_in_asset_life = True
number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint(
- asset.number_of_depreciations_booked
+ asset.opening_number_of_booked_depreciations
)
depr_schedule = get_depr_schedule(asset.name, "Active", row.finance_book)
@@ -410,7 +410,7 @@ class AssetRepair(AccountsController):
def calculate_last_schedule_date_before_modification(self, asset, row, extra_months):
asset.flags.increase_in_asset_life = True
number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint(
- asset.number_of_depreciations_booked
+ asset.opening_number_of_booked_depreciations
)
depr_schedule = get_depr_schedule(asset.name, "Active", row.finance_book)
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
index 5d4ef4e3845..8ebf9d6d389 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -125,9 +125,10 @@ def get_data(filters):
if assets_linked_to_fb and asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb:
continue
- asset_value = get_asset_value_after_depreciation(
- asset.asset_id, finance_book
- ) or get_asset_value_after_depreciation(asset.asset_id)
+ depreciation_amount = depreciation_amount_map.get(asset.asset_id) or 0.0
+ asset_value = (
+ asset.gross_purchase_amount - asset.opening_accumulated_depreciation - depreciation_amount
+ )
row = {
"asset_id": asset.asset_id,
@@ -139,7 +140,7 @@ def get_data(filters):
or pi_supplier_map.get(asset.purchase_invoice),
"gross_purchase_amount": asset.gross_purchase_amount,
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
- "depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0,
+ "depreciated_amount": depreciation_amount,
"available_for_use_date": asset.available_for_use_date,
"location": asset.location,
"asset_category": asset.asset_category,
@@ -185,11 +186,12 @@ def prepare_chart_data(data, filters):
)
for d in data:
- date = d.get(date_field)
- belongs_to_month = formatdate(date, "MMM YYYY")
+ if d.get(date_field):
+ date = d.get(date_field)
+ belongs_to_month = formatdate(date, "MMM YYYY")
- labels_values_map[belongs_to_month].asset_value += d.get("asset_value")
- labels_values_map[belongs_to_month].depreciated_amount += d.get("depreciated_amount")
+ labels_values_map[belongs_to_month].asset_value += d.get("asset_value")
+ labels_values_map[belongs_to_month].depreciated_amount += d.get("depreciated_amount")
return {
"data": {
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index 0df17fdd4b9..ff5385dd961 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -138,6 +138,7 @@ class Supplier(TransactionBase):
validate_party_accounts(self)
self.validate_internal_supplier()
self.add_role_for_user()
+ self.validate_currency_for_receivable_payable_and_advance_account()
@frappe.whitelist()
def get_supplier_group_details(self):
diff --git a/erpnext/buying/doctype/supplier/test_records.json b/erpnext/buying/doctype/supplier/test_records.json
index 1aa63fb5ba0..1bb9899cc85 100644
--- a/erpnext/buying/doctype/supplier/test_records.json
+++ b/erpnext/buying/doctype/supplier/test_records.json
@@ -35,6 +35,7 @@
"doctype": "Supplier",
"supplier_name": "_Test Supplier USD",
"supplier_group": "_Test Supplier Group",
+ "default_currency": "USD",
"accounts": [{
"company": "_Test Company",
"account": "_Test Payable USD - _TC"
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 40ce8b6bc57..f576ecc1b11 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1430,10 +1430,13 @@ class AccountsController(TransactionBase):
if d.exchange_gain_loss and (
(d.reference_doctype, d.reference_name, str(d.idx)) not in booked
):
- if self.payment_type == "Receive":
- party_account = self.paid_from
- elif self.payment_type == "Pay":
- party_account = self.paid_to
+ if self.book_advance_payments_in_separate_party_account:
+ party_account = d.account
+ else:
+ if self.payment_type == "Receive":
+ party_account = self.paid_from
+ elif self.payment_type == "Pay":
+ party_account = self.paid_to
dr_or_cr = "debit" if d.exchange_gain_loss > 0 else "credit"
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index bf6e3cd663a..30a5f38400e 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -659,10 +659,7 @@ class BuyingController(SubcontractingController):
return
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
- field = "purchase_invoice" if self.doctype == "Purchase Invoice" else "purchase_receipt"
-
self.process_fixed_asset()
- self.update_fixed_asset(field)
if self.doctype in ["Purchase Order", "Purchase Receipt"] and not frappe.db.get_single_value(
"Buying Settings", "disable_last_purchase_rate"
@@ -772,7 +769,7 @@ class BuyingController(SubcontractingController):
if not row.asset_location:
frappe.throw(_("Row {0}: Enter location for the asset item {1}").format(row.idx, row.item_code))
- item_data = frappe.db.get_value(
+ item_data = frappe.get_cached_value(
"Item", row.item_code, ["asset_naming_series", "asset_category"], as_dict=1
)
asset_quantity = row.qty if is_grouped_asset else 1
@@ -801,7 +798,7 @@ class BuyingController(SubcontractingController):
asset.flags.ignore_validate = True
asset.flags.ignore_mandatory = True
asset.set_missing_values()
- asset.insert()
+ asset.db_insert()
return asset.name
@@ -827,11 +824,7 @@ class BuyingController(SubcontractingController):
frappe.delete_doc("Asset", asset.name, force=1)
continue
- if self.docstatus in [0, 1] and not asset.get(field):
- asset.set(field, self.name)
- asset.purchase_date = self.posting_date
- asset.supplier = self.supplier
- elif self.docstatus == 2:
+ if self.docstatus == 2:
if asset.docstatus == 2:
continue
if asset.docstatus == 0:
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index e0f1ed77140..d31ee258b27 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -294,16 +294,23 @@ class SubcontractingController(StockController):
receipt_items = {item.name: item.get(self.subcontract_data.order_field) for item in receipt_items}
consumed_materials = self.__get_consumed_items(doctype, receipt_items.keys())
- voucher_nos = [d.voucher_no for d in consumed_materials if d.voucher_no]
- voucher_bundle_data = get_voucher_wise_serial_batch_from_bundle(
- voucher_no=voucher_nos,
- is_outward=1,
- get_subcontracted_item=("Subcontracting Receipt Supplied Item", "main_item_code"),
- )
-
if return_consumed_items:
return (consumed_materials, receipt_items)
+ if not consumed_materials:
+ return
+
+ voucher_nos = [d.voucher_no for d in consumed_materials if d.voucher_no]
+ voucher_bundle_data = (
+ get_voucher_wise_serial_batch_from_bundle(
+ voucher_no=voucher_nos,
+ is_outward=1,
+ get_subcontracted_item=("Subcontracting Receipt Supplied Item", "main_item_code"),
+ )
+ if voucher_nos
+ else {}
+ )
+
for row in consumed_materials:
key = (row.rm_item_code, row.main_item_code, receipt_items.get(row.reference_name))
if not self.available_materials.get(key):
@@ -350,10 +357,14 @@ class SubcontractingController(StockController):
transferred_items = self.__get_transferred_items()
voucher_nos = [row.voucher_no for row in transferred_items]
- voucher_bundle_data = get_voucher_wise_serial_batch_from_bundle(
- voucher_no=voucher_nos,
- is_outward=0,
- get_subcontracted_item=("Stock Entry Detail", "subcontracted_item"),
+ voucher_bundle_data = (
+ get_voucher_wise_serial_batch_from_bundle(
+ voucher_no=voucher_nos,
+ is_outward=0,
+ get_subcontracted_item=("Stock Entry Detail", "subcontracted_item"),
+ )
+ if voucher_nos
+ else {}
)
for row in transferred_items:
diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py
index f91acb2e4ea..3f6830c2021 100644
--- a/erpnext/controllers/tests/test_accounts_controller.py
+++ b/erpnext/controllers/tests/test_accounts_controller.py
@@ -10,6 +10,7 @@ from frappe.utils import add_days, getdate, nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.party import get_party_account
from erpnext.stock.doctype.item.test_item import create_item
@@ -55,6 +56,7 @@ class TestAccountsController(FrappeTestCase):
40 series - Company default Cost center is unset
50 series - Journals against Journals
60 series - Journals against Payment Entries
+ 70 series - Advances in Separate party account. Both Party and Advance account are in Foreign currency.
90 series - Dimension inheritence
"""
@@ -114,47 +116,102 @@ class TestAccountsController(FrappeTestCase):
self.supplier = make_supplier("_Test MC Supplier USD", "USD")
def create_account(self):
- account_name = "Debtors USD"
- if not frappe.db.get_value(
- "Account", filters={"account_name": account_name, "company": self.company}
- ):
- acc = frappe.new_doc("Account")
- acc.account_name = account_name
- acc.parent_account = "Accounts Receivable - " + self.company_abbr
- acc.company = self.company
- acc.account_currency = "USD"
- acc.account_type = "Receivable"
- acc.insert()
- else:
- name = frappe.db.get_value(
- "Account",
- filters={"account_name": account_name, "company": self.company},
- fieldname="name",
- pluck=True,
- )
- acc = frappe.get_doc("Account", name)
- self.debtors_usd = acc.name
+ accounts = [
+ frappe._dict(
+ {
+ "attribute_name": "debtors_usd",
+ "name": "Debtors USD",
+ "account_type": "Receivable",
+ "account_currency": "USD",
+ "parent_account": "Accounts Receivable - " + self.company_abbr,
+ }
+ ),
+ frappe._dict(
+ {
+ "attribute_name": "creditors_usd",
+ "name": "Creditors USD",
+ "account_type": "Payable",
+ "account_currency": "USD",
+ "parent_account": "Accounts Payable - " + self.company_abbr,
+ }
+ ),
+ # Advance accounts under Asset and Liability header
+ frappe._dict(
+ {
+ "attribute_name": "advance_received_usd",
+ "name": "Advance Received USD",
+ "account_type": "Receivable",
+ "account_currency": "USD",
+ "parent_account": "Current Liabilities - " + self.company_abbr,
+ }
+ ),
+ frappe._dict(
+ {
+ "attribute_name": "advance_paid_usd",
+ "name": "Advance Paid USD",
+ "account_type": "Payable",
+ "account_currency": "USD",
+ "parent_account": "Current Assets - " + self.company_abbr,
+ }
+ ),
+ ]
- account_name = "Creditors USD"
- if not frappe.db.get_value(
- "Account", filters={"account_name": account_name, "company": self.company}
- ):
- acc = frappe.new_doc("Account")
- acc.account_name = account_name
- acc.parent_account = "Accounts Payable - " + self.company_abbr
- acc.company = self.company
- acc.account_currency = "USD"
- acc.account_type = "Payable"
- acc.insert()
- else:
- name = frappe.db.get_value(
- "Account",
- filters={"account_name": account_name, "company": self.company},
- fieldname="name",
- pluck=True,
- )
- acc = frappe.get_doc("Account", name)
- self.creditors_usd = acc.name
+ for x in accounts:
+ if not frappe.db.get_value("Account", filters={"account_name": x.name, "company": self.company}):
+ acc = frappe.new_doc("Account")
+ acc.account_name = x.name
+ acc.parent_account = x.parent_account
+ acc.company = self.company
+ acc.account_currency = x.account_currency
+ acc.account_type = x.account_type
+ acc.insert()
+ else:
+ name = frappe.db.get_value(
+ "Account",
+ filters={"account_name": x.name, "company": self.company},
+ fieldname="name",
+ pluck=True,
+ )
+ acc = frappe.get_doc("Account", name)
+ setattr(self, x.attribute_name, acc.name)
+
+ def setup_advance_accounts_in_party_master(self):
+ company = frappe.get_doc("Company", self.company)
+ company.book_advance_payments_in_separate_party_account = 1
+ company.save()
+
+ customer = frappe.get_doc("Customer", self.customer)
+ customer.append(
+ "accounts",
+ {
+ "company": self.company,
+ "account": self.debtors_usd,
+ "advance_account": self.advance_received_usd,
+ },
+ )
+ customer.save()
+
+ supplier = frappe.get_doc("Supplier", self.supplier)
+ supplier.append(
+ "accounts",
+ {
+ "company": self.company,
+ "account": self.creditors_usd,
+ "advance_account": self.advance_paid_usd,
+ },
+ )
+ supplier.save()
+
+ def remove_advance_accounts_from_party_master(self):
+ company = frappe.get_doc("Company", self.company)
+ company.book_advance_payments_in_separate_party_account = 0
+ company.save()
+ customer = frappe.get_doc("Customer", self.customer)
+ customer.accounts = []
+ customer.save()
+ supplier = frappe.get_doc("Supplier", self.supplier)
+ supplier.accounts = []
+ supplier.save()
def create_sales_invoice(
self,
@@ -218,6 +275,48 @@ class TestAccountsController(FrappeTestCase):
payment.posting_date = posting_date
return payment
+ def create_purchase_invoice(
+ self,
+ qty=1,
+ rate=1,
+ conversion_rate=80,
+ posting_date=None,
+ do_not_save=False,
+ do_not_submit=False,
+ ):
+ """
+ Helper function to populate default values in purchase invoice
+ """
+ if posting_date is None:
+ posting_date = nowdate()
+
+ pinv = make_purchase_invoice(
+ posting_date=posting_date,
+ qty=qty,
+ rate=rate,
+ company=self.company,
+ supplier=self.supplier,
+ item_code=self.item,
+ item_name=self.item,
+ cost_center=self.cost_center,
+ warehouse=self.warehouse,
+ parent_cost_center=self.cost_center,
+ update_stock=0,
+ currency="USD",
+ conversion_rate=conversion_rate,
+ is_pos=0,
+ is_return=0,
+ income_account=self.income_account,
+ expense_account=self.expense_account,
+ do_not_save=True,
+ )
+ pinv.credit_to = self.creditors_usd
+ if not do_not_save:
+ pinv.save()
+ if not do_not_submit:
+ pinv.submit()
+ return pinv
+
def clear_old_entries(self):
doctype_list = [
"GL Entry",
@@ -1698,3 +1797,123 @@ class TestAccountsController(FrappeTestCase):
# Exchange Gain/Loss Journal should've been cancelled
exc_je_for_je1 = self.get_journals_for(je1.doctype, je1.name)
self.assertEqual(exc_je_for_je1, [])
+
+ def test_70_advance_payment_against_sales_invoice_in_foreign_currency(self):
+ """
+ Customer advance booked under Liability
+ """
+ self.setup_advance_accounts_in_party_master()
+
+ adv = self.create_payment_entry(amount=1, source_exc_rate=83)
+ adv.save() # explicit 'save' is needed to trigger set_liability_account()
+ self.assertEqual(adv.paid_from, self.advance_received_usd)
+ adv.submit()
+
+ si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1, do_not_submit=True)
+ si.debit_to = self.debtors_usd
+ si.save().submit()
+ self.assert_ledger_outstanding(si.doctype, si.name, 80.0, 1.0)
+
+ pr = self.create_payment_reconciliation()
+ pr.receivable_payable_account = self.debtors_usd
+ pr.default_advance_account = self.advance_received_usd
+ pr.get_unreconciled_entries()
+ self.assertEqual(pr.invoices[0].invoice_number, si.name)
+ self.assertEqual(pr.payments[0].reference_name, adv.name)
+
+ # Allocate and Reconcile
+ invoices = [x.as_dict() for x in pr.invoices]
+ payments = [x.as_dict() for x in pr.payments]
+ pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+ pr.reconcile()
+ self.assertEqual(len(pr.invoices), 0)
+ self.assertEqual(len(pr.payments), 0)
+ self.assert_ledger_outstanding(si.doctype, si.name, 0.0, 0.0)
+
+ # Exc Gain/Loss journal should've been creatad
+ exc_je_for_si = self.get_journals_for(si.doctype, si.name)
+ exc_je_for_adv = self.get_journals_for(adv.doctype, adv.name)
+ self.assertEqual(len(exc_je_for_si), 1)
+ self.assertEqual(len(exc_je_for_adv), 1)
+ self.assertEqual(exc_je_for_si, exc_je_for_adv)
+
+ adv.reload()
+ adv.cancel()
+ si.reload()
+ self.assert_ledger_outstanding(si.doctype, si.name, 80.0, 1.0)
+ # Exc Gain/Loss journal should've been cancelled
+ exc_je_for_si = self.get_journals_for(si.doctype, si.name)
+ exc_je_for_adv = self.get_journals_for(adv.doctype, adv.name)
+ self.assertEqual(len(exc_je_for_si), 0)
+ self.assertEqual(len(exc_je_for_adv), 0)
+
+ self.remove_advance_accounts_from_party_master()
+
+ def test_71_advance_payment_against_purchase_invoice_in_foreign_currency(self):
+ """
+ Supplier advance booked under Asset
+ """
+ self.setup_advance_accounts_in_party_master()
+
+ usd_amount = 1
+ inr_amount = 85
+ exc_rate = 85
+ adv = create_payment_entry(
+ company=self.company,
+ payment_type="Pay",
+ party_type="Supplier",
+ party=self.supplier,
+ paid_from=self.cash,
+ paid_to=self.advance_paid_usd,
+ paid_amount=inr_amount,
+ )
+ adv.source_exchange_rate = 1
+ adv.target_exchange_rate = exc_rate
+ adv.received_amount = usd_amount
+ adv.paid_amount = exc_rate * usd_amount
+ adv.posting_date = nowdate()
+ adv.save()
+ # Make sure that advance account is still set
+ self.assertEqual(adv.paid_to, self.advance_paid_usd)
+ adv.submit()
+
+ pi = self.create_purchase_invoice(qty=1, conversion_rate=83, rate=1)
+ self.assertEqual(pi.credit_to, self.creditors_usd)
+ self.assert_ledger_outstanding(pi.doctype, pi.name, 83.0, 1.0)
+
+ pr = self.create_payment_reconciliation()
+ pr.party_type = "Supplier"
+ pr.party = self.supplier
+ pr.receivable_payable_account = self.creditors_usd
+ pr.default_advance_account = self.advance_paid_usd
+ pr.get_unreconciled_entries()
+ self.assertEqual(pr.invoices[0].invoice_number, pi.name)
+ self.assertEqual(pr.payments[0].reference_name, adv.name)
+
+ # Allocate and Reconcile
+ invoices = [x.as_dict() for x in pr.invoices]
+ payments = [x.as_dict() for x in pr.payments]
+ pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+ pr.reconcile()
+ self.assertEqual(len(pr.invoices), 0)
+ self.assertEqual(len(pr.payments), 0)
+ self.assert_ledger_outstanding(pi.doctype, pi.name, 0.0, 0.0)
+
+ # Exc Gain/Loss journal should've been creatad
+ exc_je_for_pi = self.get_journals_for(pi.doctype, pi.name)
+ exc_je_for_adv = self.get_journals_for(adv.doctype, adv.name)
+ self.assertEqual(len(exc_je_for_pi), 1)
+ self.assertEqual(len(exc_je_for_adv), 1)
+ self.assertEqual(exc_je_for_pi, exc_je_for_adv)
+
+ adv.reload()
+ adv.cancel()
+ pi.reload()
+ self.assert_ledger_outstanding(pi.doctype, pi.name, 83.0, 1.0)
+ # Exc Gain/Loss journal should've been cancelled
+ exc_je_for_pi = self.get_journals_for(pi.doctype, pi.name)
+ exc_je_for_adv = self.get_journals_for(adv.doctype, adv.name)
+ self.assertEqual(len(exc_je_for_pi), 0)
+ self.assertEqual(len(exc_je_for_adv), 0)
+
+ self.remove_advance_accounts_from_party_master()
diff --git a/erpnext/crm/doctype/lead/lead_list.js b/erpnext/crm/doctype/lead/lead_list.js
index 97415251a93..beef2c934dc 100644
--- a/erpnext/crm/doctype/lead/lead_list.js
+++ b/erpnext/crm/doctype/lead/lead_list.js
@@ -1,4 +1,8 @@
frappe.listview_settings["Lead"] = {
+ get_indicator: function (doc) {
+ var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status];
+ return indicator;
+ },
onload: function (listview) {
if (frappe.boot.user.can_create.includes("Prospect")) {
listview.page.add_action_item(__("Create Prospect"), function () {
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 2522077e9c3..dba491a728d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -365,3 +365,5 @@ erpnext.patches.v15_0.fix_debit_credit_in_transaction_currency
erpnext.patches.v15_0.remove_cancelled_asset_capitalization_from_asset
erpnext.patches.v15_0.rename_purchase_receipt_amount_to_purchase_amount
erpnext.patches.v14_0.enable_set_priority_for_pricing_rules #1
+erpnext.patches.v15_0.rename_number_of_depreciations_booked_to_opening_booked_depreciations
+erpnext.patches.v15_0.update_total_number_of_booked_depreciations
diff --git a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
index c31d754d2cd..523b559d734 100644
--- a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
+++ b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py
@@ -4,6 +4,7 @@ import frappe
def execute():
frappe.reload_doc("assets", "doctype", "Asset Depreciation Schedule")
frappe.reload_doc("assets", "doctype", "Asset Finance Book")
+ frappe.reload_doc("assets", "doctype", "Asset")
assets = get_details_of_draft_or_submitted_depreciable_assets()
@@ -43,7 +44,7 @@ def get_details_of_draft_or_submitted_depreciable_assets():
asset.name,
asset.opening_accumulated_depreciation,
asset.gross_purchase_amount,
- asset.number_of_depreciations_booked,
+ asset.opening_number_of_booked_depreciations,
asset.docstatus,
)
.where(asset.calculate_depreciation == 1)
diff --git a/erpnext/patches/v15_0/rename_number_of_depreciations_booked_to_opening_booked_depreciations.py b/erpnext/patches/v15_0/rename_number_of_depreciations_booked_to_opening_booked_depreciations.py
new file mode 100644
index 00000000000..18183374554
--- /dev/null
+++ b/erpnext/patches/v15_0/rename_number_of_depreciations_booked_to_opening_booked_depreciations.py
@@ -0,0 +1,7 @@
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+ if frappe.db.has_column("Asset", "number_of_depreciations_booked"):
+ rename_field("Asset", "number_of_depreciations_booked", "opening_number_of_booked_depreciations")
diff --git a/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py b/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py
index afb59e0f6f5..f8cb3e48e7a 100644
--- a/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py
+++ b/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py
@@ -3,18 +3,19 @@ import frappe
def execute():
# not using frappe.qb because https://github.com/frappe/frappe/issues/20292
+ # nosemgrep
frappe.db.sql(
"""UPDATE `tabAsset Depreciation Schedule`
JOIN `tabAsset`
ON `tabAsset Depreciation Schedule`.`asset`=`tabAsset`.`name`
SET
`tabAsset Depreciation Schedule`.`gross_purchase_amount`=`tabAsset`.`gross_purchase_amount`,
- `tabAsset Depreciation Schedule`.`number_of_depreciations_booked`=`tabAsset`.`number_of_depreciations_booked`
+ `tabAsset Depreciation Schedule`.`opening_number_of_booked_depreciations`=`tabAsset`.`opening_number_of_booked_depreciations`
WHERE
(
`tabAsset Depreciation Schedule`.`gross_purchase_amount`<>`tabAsset`.`gross_purchase_amount`
OR
- `tabAsset Depreciation Schedule`.`number_of_depreciations_booked`<>`tabAsset`.`number_of_depreciations_booked`
+ `tabAsset Depreciation Schedule`.`opening_number_of_booked_depreciations`<>`tabAsset`.`opening_number_of_booked_depreciations`
)
AND `tabAsset Depreciation Schedule`.`docstatus`<2"""
)
diff --git a/erpnext/patches/v15_0/update_total_number_of_booked_depreciations.py b/erpnext/patches/v15_0/update_total_number_of_booked_depreciations.py
new file mode 100644
index 00000000000..4b556c2b512
--- /dev/null
+++ b/erpnext/patches/v15_0/update_total_number_of_booked_depreciations.py
@@ -0,0 +1,29 @@
+import frappe
+
+from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
+ get_depr_schedule,
+)
+
+
+def execute():
+ if frappe.db.has_column("Asset Finance Book", "total_number_of_booked_depreciations"):
+ assets = frappe.get_all(
+ "Asset", filters={"docstatus": 1}, fields=["name", "opening_number_of_booked_depreciations"]
+ )
+
+ for asset in assets:
+ asset_doc = frappe.get_doc("Asset", asset.name)
+
+ for fb_row in asset_doc.get("finance_books"):
+ depr_schedule = get_depr_schedule(asset.name, "Active", fb_row.finance_book)
+ total_number_of_booked_depreciations = asset.opening_number_of_booked_depreciations or 0
+
+ for je in depr_schedule:
+ if je.journal_entry:
+ total_number_of_booked_depreciations += 1
+ frappe.db.set_value(
+ "Asset Finance Book",
+ fb_row.name,
+ "total_number_of_booked_depreciations",
+ total_number_of_booked_depreciations,
+ )
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 139ab4bff0f..950914953aa 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -325,7 +325,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}
const me = this;
- if (!this.frm.is_new() && this.frm.doc.docstatus === 0) {
+ if (!this.frm.is_new() && this.frm.doc.docstatus === 0 && frappe.model.can_create("Quality Inspection")) {
this.frm.add_custom_button(__("Quality Inspection(s)"), () => {
me.make_quality_inspection();
}, __("Create"));
@@ -1733,6 +1733,10 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
me.frm.doc.items.forEach(d => {
if (in_list(JSON.parse(data.apply_rule_on_other_items), d[data.apply_rule_on])) {
for(var k in data) {
+ if (data.pricing_rule_for == "Discount Percentage" && data.apply_rule_on_other_items && k == "discount_amount") {
+ continue;
+ }
+
if (in_list(fields, k) && data[k] && (data.price_or_product_discount === 'Price' || k === 'pricing_rules')) {
frappe.model.set_value(d.doctype, d.name, k, data[k]);
}
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index 4928f2dc1a5..78efb46f4c3 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -635,6 +635,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
set_data(data) {
data.forEach((d) => {
d.qty = Math.abs(d.qty);
+ d.name = d.child_row || d.name;
this.dialog.fields_dict.entries.df.data.push(d);
});
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index f5185c2ff5b..8ce67cc659a 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -144,6 +144,7 @@ class Customer(TransactionBase):
self.validate_default_bank_account()
self.validate_internal_customer()
self.add_role_for_user()
+ self.validate_currency_for_receivable_payable_and_advance_account()
# set loyalty program tier
if frappe.db.exists("Customer", self.name):
diff --git a/erpnext/selling/doctype/customer/test_records.json b/erpnext/selling/doctype/customer/test_records.json
index 61cb36b0fae..6040f4dd75b 100644
--- a/erpnext/selling/doctype/customer/test_records.json
+++ b/erpnext/selling/doctype/customer/test_records.json
@@ -47,6 +47,7 @@
"customer_type": "Individual",
"doctype": "Customer",
"territory": "_Test Territory",
+ "default_currency": "USD",
"accounts": [{
"company": "_Test Company",
"account": "_Test Receivable USD - _TC"
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 5885d092cce..cdcd1047bd8 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -58,7 +58,8 @@ frappe.ui.form.on("Sales Order", {
if (
frm.doc.status !== "Closed" &&
flt(frm.doc.per_delivered, 2) < 100 &&
- flt(frm.doc.per_billed, 2) < 100
+ flt(frm.doc.per_billed, 2) < 100 &&
+ frm.has_perm("write")
) {
frm.add_custom_button(__("Update Items"), () => {
erpnext.utils.update_child_items({
@@ -85,7 +86,11 @@ frappe.ui.form.on("Sales Order", {
}
// Stock Reservation > Unreserve button will be only visible if the SO has un-delivered reserved stock.
- if (frm.doc.__onload && frm.doc.__onload.has_reserved_stock) {
+ if (
+ frm.doc.__onload &&
+ frm.doc.__onload.has_reserved_stock &&
+ frappe.model.can_cancel("Stock Reservation Entry")
+ ) {
frm.add_custom_button(
__("Unreserve"),
() => frm.events.cancel_stock_reservation_entries(frm),
@@ -94,7 +99,7 @@ frappe.ui.form.on("Sales Order", {
}
frm.doc.items.forEach((item) => {
- if (flt(item.stock_reserved_qty) > 0) {
+ if (flt(item.stock_reserved_qty) > 0 && frappe.model.can_read("Stock Reservation Entry")) {
frm.add_custom_button(
__("Reserved Stock"),
() => frm.events.show_reserved_stock(frm),
@@ -142,6 +147,10 @@ frappe.ui.form.on("Sales Order", {
},
get_items_from_internal_purchase_order(frm) {
+ if (!frappe.model.can_read("Purchase Order")) {
+ return;
+ }
+
frm.add_custom_button(
__("Purchase Order"),
() => {
@@ -634,15 +643,17 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
}
}
- if (!doc.__onload || !doc.__onload.has_reserved_stock) {
- // Don't show the `Reserve` button if the Sales Order has Picked Items.
- if (flt(doc.per_picked, 2) < 100 && flt(doc.per_delivered, 2) < 100) {
- this.frm.add_custom_button(
- __("Pick List"),
- () => this.create_pick_list(),
- __("Create")
- );
- }
+ if (
+ (!doc.__onload || !doc.__onload.has_reserved_stock) &&
+ flt(doc.per_picked, 2) < 100 &&
+ flt(doc.per_delivered, 2) < 100 &&
+ frappe.model.can_create("Pick List")
+ ) {
+ this.frm.add_custom_button(
+ __("Pick List"),
+ () => this.create_pick_list(),
+ __("Create")
+ );
}
const order_is_a_sale = ["Sales", "Shopping Cart"].indexOf(doc.order_type) !== -1;
@@ -657,20 +668,25 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
(order_is_a_sale || order_is_a_custom_sale) &&
allow_delivery
) {
- this.frm.add_custom_button(
- __("Delivery Note"),
- () => this.make_delivery_note_based_on_delivery_date(true),
- __("Create")
- );
- this.frm.add_custom_button(
- __("Work Order"),
- () => this.make_work_order(),
- __("Create")
- );
+ if (frappe.model.can_create("Delivery Note")) {
+ this.frm.add_custom_button(
+ __("Delivery Note"),
+ () => this.make_delivery_note_based_on_delivery_date(true),
+ __("Create")
+ );
+ }
+
+ if (frappe.model.can_create("Work Order")) {
+ this.frm.add_custom_button(
+ __("Work Order"),
+ () => this.make_work_order(),
+ __("Create")
+ );
+ }
}
// sales invoice
- if (flt(doc.per_billed, 2) < 100) {
+ if (flt(doc.per_billed, 2) < 100 && frappe.model.can_create("Sales Invoice")) {
this.frm.add_custom_button(
__("Sales Invoice"),
() => me.make_sales_invoice(),
@@ -680,8 +696,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
// material request
if (
- !doc.order_type ||
- ((order_is_a_sale || order_is_a_custom_sale) && flt(doc.per_delivered, 2) < 100)
+ (!doc.order_type ||
+ ((order_is_a_sale || order_is_a_custom_sale) &&
+ flt(doc.per_delivered, 2) < 100)) &&
+ frappe.model.can_create("Material Request")
) {
this.frm.add_custom_button(
__("Material Request"),
@@ -696,7 +714,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
}
// Make Purchase Order
- if (!this.frm.doc.is_internal_customer) {
+ if (!this.frm.doc.is_internal_customer && frappe.model.can_create("Purchase Order")) {
this.frm.add_custom_button(
__("Purchase Order"),
() => this.make_purchase_order(),
@@ -706,24 +724,32 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
// maintenance
if (flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) {
- this.frm.add_custom_button(
- __("Maintenance Visit"),
- () => this.make_maintenance_visit(),
- __("Create")
- );
- this.frm.add_custom_button(
- __("Maintenance Schedule"),
- () => this.make_maintenance_schedule(),
- __("Create")
- );
+ if (frappe.model.can_create("Maintenance Visit")) {
+ this.frm.add_custom_button(
+ __("Maintenance Visit"),
+ () => this.make_maintenance_visit(),
+ __("Create")
+ );
+ }
+ if (frappe.model.can_create("Maintenance Schedule")) {
+ this.frm.add_custom_button(
+ __("Maintenance Schedule"),
+ () => this.make_maintenance_schedule(),
+ __("Create")
+ );
+ }
}
// project
- if (flt(doc.per_delivered, 2) < 100) {
+ if (flt(doc.per_delivered, 2) < 100 && frappe.model.can_create("Project")) {
this.frm.add_custom_button(__("Project"), () => this.make_project(), __("Create"));
}
- if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
+ if (
+ doc.docstatus === 1 &&
+ !doc.inter_company_order_reference &&
+ frappe.model.can_create("Purchase Order")
+ ) {
let me = this;
let internal = me.frm.doc.is_internal_customer;
if (internal) {
@@ -752,13 +778,20 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
() => this.make_payment_request(),
__("Create")
);
- this.frm.add_custom_button(__("Payment"), () => this.make_payment_entry(), __("Create"));
+
+ if (frappe.model.can_create("Payment Entry")) {
+ this.frm.add_custom_button(
+ __("Payment"),
+ () => this.make_payment_entry(),
+ __("Create")
+ );
+ }
}
this.frm.page.set_inner_btn_group_as_primary(__("Create"));
}
}
- if (this.frm.doc.docstatus === 0) {
+ if (this.frm.doc.docstatus === 0 && frappe.model.can_read("Quotation")) {
this.frm.add_custom_button(
__("Quotation"),
function () {
diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py
index 0b783c0c56e..6d95d3bcb24 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.py
+++ b/erpnext/setup/doctype/customer_group/customer_group.py
@@ -38,6 +38,53 @@ class CustomerGroup(NestedSet):
def validate(self):
if not self.parent_customer_group:
self.parent_customer_group = get_root_of("Customer Group")
+ self.validate_currency_for_receivable_and_advance_account()
+
+ def validate_currency_for_receivable_and_advance_account(self):
+ for x in self.accounts:
+ company_default_currency = frappe.get_cached_value("Company", x.company, "default_currency")
+ receivable_account_currency = None
+ advance_account_currency = None
+
+ if x.account:
+ receivable_account_currency = frappe.get_cached_value(
+ "Account", x.account, "account_currency"
+ )
+
+ if x.advance_account:
+ advance_account_currency = frappe.get_cached_value(
+ "Account", x.advance_account, "account_currency"
+ )
+
+ if receivable_account_currency and receivable_account_currency != company_default_currency:
+ frappe.throw(
+ _("Receivable Account: {0} must be in Company default currency: {1}").format(
+ frappe.bold(x.account),
+ frappe.bold(company_default_currency),
+ )
+ )
+
+ if advance_account_currency and advance_account_currency != company_default_currency:
+ frappe.throw(
+ _("Advance Account: {0} must be in Company default currency: {1}").format(
+ frappe.bold(x.advance_account), frappe.bold(company_default_currency)
+ )
+ )
+
+ if (
+ receivable_account_currency
+ and advance_account_currency
+ and receivable_account_currency != advance_account_currency
+ ):
+ frappe.throw(
+ _(
+ "Both Receivable Account: {0} and Advance Account: {1} must be of same currency for company: {2}"
+ ).format(
+ frappe.bold(x.account),
+ frappe.bold(x.advance_account),
+ frappe.bold(x.company),
+ )
+ )
def on_update(self):
self.validate_name_with_customer()
diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.py b/erpnext/setup/doctype/supplier_group/supplier_group.py
index b639b962509..fa0c6beac49 100644
--- a/erpnext/setup/doctype/supplier_group/supplier_group.py
+++ b/erpnext/setup/doctype/supplier_group/supplier_group.py
@@ -3,6 +3,7 @@
import frappe
+from frappe import _
from frappe.utils.nestedset import NestedSet, get_root_of
@@ -32,6 +33,51 @@ class SupplierGroup(NestedSet):
def validate(self):
if not self.parent_supplier_group:
self.parent_supplier_group = get_root_of("Supplier Group")
+ self.validate_currency_for_payable_and_advance_account()
+
+ def validate_currency_for_payable_and_advance_account(self):
+ for x in self.accounts:
+ company_default_currency = frappe.get_cached_value("Company", x.company, "default_currency")
+ payable_account_currency = None
+ advance_account_currency = None
+
+ if x.account:
+ payable_account_currency = frappe.get_cached_value("Account", x.account, "account_currency")
+
+ if x.advance_account:
+ advance_account_currency = frappe.get_cached_value(
+ "Account", x.advance_account, "account_currency"
+ )
+
+ if payable_account_currency and payable_account_currency != company_default_currency:
+ frappe.throw(
+ _("Payable Account: {0} must be in Company default currency: {1}").format(
+ frappe.bold(x.account),
+ frappe.bold(company_default_currency),
+ )
+ )
+
+ if advance_account_currency and advance_account_currency != company_default_currency:
+ frappe.throw(
+ _("Advance Account: {0} must be in Company default currency: {1}").format(
+ frappe.bold(x.advance_account), frappe.bold(company_default_currency)
+ )
+ )
+
+ if (
+ payable_account_currency
+ and advance_account_currency
+ and payable_account_currency != advance_account_currency
+ ):
+ frappe.throw(
+ _(
+ "Both Payable Account: {0} and Advance Account: {1} must be of same currency for company: {2}"
+ ).format(
+ frappe.bold(x.account),
+ frappe.bold(x.advance_account),
+ frappe.bold(x.company),
+ )
+ )
def on_update(self):
NestedSet.on_update(self)
diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py
index e43e6f21c92..d10833b6d46 100644
--- a/erpnext/stock/deprecated_serial_batch.py
+++ b/erpnext/stock/deprecated_serial_batch.py
@@ -1,3 +1,6 @@
+import datetime
+from collections import defaultdict
+
import frappe
from frappe.query_builder.functions import CombineDatetime, Sum
from frappe.utils import flt
@@ -8,12 +11,7 @@ from pypika import Order
class DeprecatedSerialNoValuation:
@deprecated
def calculate_stock_value_from_deprecarated_ledgers(self):
- if not frappe.db.get_all(
- "Stock Ledger Entry",
- fields=["name"],
- filters={"serial_no": ("is", "set"), "is_cancelled": 0, "item_code": self.sle.item_code},
- limit=1,
- ):
+ if not has_sle_for_serial_nos(self.sle.item_code):
return
serial_nos = self.get_filterd_serial_nos()
@@ -82,6 +80,20 @@ class DeprecatedSerialNoValuation:
return incoming_values
+@frappe.request_cache
+def has_sle_for_serial_nos(item_code):
+ serial_nos = frappe.db.get_all(
+ "Stock Ledger Entry",
+ fields=["name"],
+ filters={"serial_no": ("is", "set"), "is_cancelled": 0, "item_code": item_code},
+ limit=1,
+ )
+ if serial_nos:
+ return True
+
+ return False
+
+
class DeprecatedBatchNoValuation:
@deprecated
def calculate_avg_rate_from_deprecarated_ledgers(self):
@@ -92,19 +104,25 @@ class DeprecatedBatchNoValuation:
@deprecated
def get_sle_for_batches(self):
+ from erpnext.stock.utils import get_combine_datetime
+
if not self.batchwise_valuation_batches:
return []
sle = frappe.qb.DocType("Stock Ledger Entry")
- timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
- self.sle.posting_date, self.sle.posting_time
- )
- if self.sle.creation:
- timestamp_condition |= (
- CombineDatetime(sle.posting_date, sle.posting_time)
- == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
- ) & (sle.creation < self.sle.creation)
+ timestamp_condition = None
+ if self.sle.posting_date and self.sle.posting_time:
+ posting_datetime = get_combine_datetime(self.sle.posting_date, self.sle.posting_time)
+ if not self.sle.creation:
+ posting_datetime = posting_datetime + datetime.timedelta(milliseconds=1)
+
+ timestamp_condition = sle.posting_datetime < posting_datetime
+
+ if self.sle.creation:
+ timestamp_condition |= (sle.posting_datetime == posting_datetime) & (
+ sle.creation < self.sle.creation
+ )
query = (
frappe.qb.from_(sle)
@@ -120,10 +138,12 @@ class DeprecatedBatchNoValuation:
& (sle.batch_no.isnotnull())
& (sle.is_cancelled == 0)
)
- .where(timestamp_condition)
.groupby(sle.batch_no)
)
+ if timestamp_condition:
+ query = query.where(timestamp_condition)
+
if self.sle.name:
query = query.where(sle.name != self.sle.name)
@@ -134,8 +154,8 @@ class DeprecatedBatchNoValuation:
if not self.non_batchwise_valuation_batches:
return
- self.non_batchwise_balance_value = 0.0
- self.non_batchwise_balance_qty = 0.0
+ self.non_batchwise_balance_value = defaultdict(float)
+ self.non_batchwise_balance_qty = defaultdict(float)
self.set_balance_value_for_non_batchwise_valuation_batches()
@@ -146,12 +166,12 @@ class DeprecatedBatchNoValuation:
if not self.non_batchwise_balance_qty:
continue
- if self.non_batchwise_balance_value == 0:
+ if self.non_batchwise_balance_qty.get(batch_no) == 0:
self.batch_avg_rate[batch_no] = 0.0
self.stock_value_differece[batch_no] = 0.0
else:
self.batch_avg_rate[batch_no] = (
- self.non_batchwise_balance_value / self.non_batchwise_balance_qty
+ self.non_batchwise_balance_value[batch_no] / self.non_batchwise_balance_qty[batch_no]
)
self.stock_value_differece[batch_no] = self.non_batchwise_balance_value
@@ -174,17 +194,21 @@ class DeprecatedBatchNoValuation:
@deprecated
def set_balance_value_from_sl_entries(self) -> None:
+ from erpnext.stock.utils import get_combine_datetime
+
sle = frappe.qb.DocType("Stock Ledger Entry")
batch = frappe.qb.DocType("Batch")
- timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
- self.sle.posting_date, self.sle.posting_time
- )
+ posting_datetime = get_combine_datetime(self.sle.posting_date, self.sle.posting_time)
+ if not self.sle.creation:
+ posting_datetime = posting_datetime + datetime.timedelta(milliseconds=1)
+
+ timestamp_condition = sle.posting_datetime < posting_datetime
+
if self.sle.creation:
- timestamp_condition |= (
- CombineDatetime(sle.posting_date, sle.posting_time)
- == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
- ) & (sle.creation < self.sle.creation)
+ timestamp_condition |= (sle.posting_datetime == posting_datetime) & (
+ sle.creation < self.sle.creation
+ )
query = (
frappe.qb.from_(sle)
@@ -201,6 +225,7 @@ class DeprecatedBatchNoValuation:
& (sle.batch_no.isnotnull())
& (batch.use_batchwise_valuation == 0)
& (sle.is_cancelled == 0)
+ & (sle.batch_no.isin(self.non_batchwise_valuation_batches))
)
.where(timestamp_condition)
.groupby(sle.batch_no)
@@ -210,8 +235,8 @@ class DeprecatedBatchNoValuation:
query = query.where(sle.name != self.sle.name)
for d in query.run(as_dict=True):
- self.non_batchwise_balance_value += flt(d.batch_value)
- self.non_batchwise_balance_qty += flt(d.batch_qty)
+ self.non_batchwise_balance_value[d.batch_no] += flt(d.batch_value)
+ self.non_batchwise_balance_qty[d.batch_no] += flt(d.batch_qty)
self.available_qty[d.batch_no] += flt(d.batch_qty)
@deprecated
@@ -249,6 +274,7 @@ class DeprecatedBatchNoValuation:
& (bundle.is_cancelled == 0)
& (bundle.docstatus == 1)
& (bundle.type_of_transaction.isin(["Inward", "Outward"]))
+ & (bundle_child.batch_no.isin(self.non_batchwise_valuation_batches))
)
.where(timestamp_condition)
.groupby(bundle_child.batch_no)
@@ -260,6 +286,6 @@ class DeprecatedBatchNoValuation:
query = query.where(bundle.voucher_type != "Pick List")
for d in query.run(as_dict=True):
- self.non_batchwise_balance_value += flt(d.batch_value)
- self.non_batchwise_balance_qty += flt(d.batch_qty)
+ self.non_batchwise_balance_value[d.batch_no] += flt(d.batch_value)
+ self.non_batchwise_balance_qty[d.batch_no] += flt(d.batch_qty)
self.available_qty[d.batch_no] += flt(d.batch_qty)
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index 0be85e46015..e490badfc40 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -161,11 +161,25 @@ class Batch(Document):
self.use_batchwise_valuation = 1
def before_save(self):
+ self.set_expiry_date()
+
+ def set_expiry_date(self):
has_expiry_date, shelf_life_in_days = frappe.db.get_value(
"Item", self.item, ["has_expiry_date", "shelf_life_in_days"]
)
+
if not self.expiry_date and has_expiry_date and shelf_life_in_days:
- self.expiry_date = add_days(self.manufacturing_date, shelf_life_in_days)
+ if (
+ not self.manufacturing_date
+ and self.reference_doctype in ["Stock Entry", "Purchase Receipt", "Purchase Invoice"]
+ and self.reference_name
+ ):
+ self.manufacturing_date = frappe.db.get_value(
+ self.reference_doctype, self.reference_name, "posting_date"
+ )
+
+ if self.manufacturing_date:
+ self.expiry_date = add_days(self.manufacturing_date, shelf_life_in_days)
if has_expiry_date and not self.expiry_date:
frappe.throw(
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 23d0adc5708..3352b53343a 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -79,7 +79,12 @@ frappe.ui.form.on("Delivery Note", {
},
refresh: function (frm) {
- if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) {
+ if (
+ frm.doc.docstatus === 1 &&
+ frm.doc.is_return === 1 &&
+ frm.doc.per_billed !== 100 &&
+ frappe.model.can_create("Sales Invoice")
+ ) {
frm.add_custom_button(
__("Credit Note"),
function () {
@@ -93,7 +98,11 @@ frappe.ui.form.on("Delivery Note", {
frm.page.set_inner_btn_group_as_primary(__("Create"));
}
- if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
+ if (
+ frm.doc.docstatus == 1 &&
+ !frm.doc.inter_company_reference &&
+ frappe.model.can_create("Purchase Receipt")
+ ) {
let internal = frm.doc.is_internal_customer;
if (internal) {
let button_label =
@@ -140,43 +149,47 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends (
refresh(doc, dt, dn) {
var me = this;
super.refresh();
- if (!doc.is_return && (doc.status != "Closed" || this.frm.is_new())) {
- if (this.frm.doc.docstatus === 0) {
- this.frm.add_custom_button(
- __("Sales Order"),
- function () {
- if (!me.frm.doc.customer) {
- frappe.throw({
- title: __("Mandatory"),
- message: __("Please Select a Customer"),
- });
- }
- erpnext.utils.map_current_doc({
- method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
- args: {
- for_reserved_stock: 1,
- },
- source_doctype: "Sales Order",
- target: me.frm,
- setters: {
- customer: me.frm.doc.customer,
- },
- get_query_filters: {
- docstatus: 1,
- status: ["not in", ["Closed", "On Hold"]],
- per_delivered: ["<", 99.99],
- company: me.frm.doc.company,
- project: me.frm.doc.project || undefined,
- },
+ if (
+ !doc.is_return &&
+ (doc.status != "Closed" || this.frm.is_new()) &&
+ this.frm.has_perm("write") &&
+ frappe.model.can_read("Sales Order") &&
+ this.frm.doc.docstatus === 0
+ ) {
+ this.frm.add_custom_button(
+ __("Sales Order"),
+ function () {
+ if (!me.frm.doc.customer) {
+ frappe.throw({
+ title: __("Mandatory"),
+ message: __("Please Select a Customer"),
});
- },
- __("Get Items From")
- );
- }
+ }
+ erpnext.utils.map_current_doc({
+ method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
+ args: {
+ for_reserved_stock: 1,
+ },
+ source_doctype: "Sales Order",
+ target: me.frm,
+ setters: {
+ customer: me.frm.doc.customer,
+ },
+ get_query_filters: {
+ docstatus: 1,
+ status: ["not in", ["Closed", "On Hold"]],
+ per_delivered: ["<", 99.99],
+ company: me.frm.doc.company,
+ project: me.frm.doc.project || undefined,
+ },
+ });
+ },
+ __("Get Items From")
+ );
}
if (!doc.is_return && doc.status != "Closed") {
- if (doc.docstatus == 1) {
+ if (doc.docstatus == 1 && frappe.model.can_create("Shipment")) {
this.frm.add_custom_button(
__("Shipment"),
function () {
@@ -186,7 +199,11 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends (
);
}
- if (flt(doc.per_installed, 2) < 100 && doc.docstatus == 1)
+ if (
+ flt(doc.per_installed, 2) < 100 &&
+ doc.docstatus == 1 &&
+ frappe.model.can_create("Installation Note")
+ ) {
this.frm.add_custom_button(
__("Installation Note"),
function () {
@@ -194,8 +211,9 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends (
},
__("Create")
);
+ }
- if (doc.docstatus == 1) {
+ if (doc.docstatus == 1 && this.frm.has_perm("create")) {
this.frm.add_custom_button(
__("Sales Return"),
function () {
@@ -205,7 +223,7 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends (
);
}
- if (doc.docstatus == 1) {
+ if (doc.docstatus == 1 && frappe.model.can_create("Delivery Trip")) {
this.frm.add_custom_button(
__("Delivery Trip"),
function () {
@@ -215,19 +233,23 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends (
);
}
- if (doc.docstatus == 0 && !doc.__islocal) {
- if (doc.__onload && doc.__onload.has_unpacked_items) {
- this.frm.add_custom_button(
- __("Packing Slip"),
- function () {
- frappe.model.open_mapped_doc({
- method: "erpnext.stock.doctype.delivery_note.delivery_note.make_packing_slip",
- frm: me.frm,
- });
- },
- __("Create")
- );
- }
+ if (
+ doc.docstatus == 0 &&
+ !doc.__islocal &&
+ doc.__onload &&
+ doc.__onload.has_unpacked_items &&
+ frappe.model.can_create("Packing Slip")
+ ) {
+ this.frm.add_custom_button(
+ __("Packing Slip"),
+ function () {
+ frappe.model.open_mapped_doc({
+ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_packing_slip",
+ frm: me.frm,
+ });
+ },
+ __("Create")
+ );
}
if (!doc.__islocal && doc.docstatus == 1) {
@@ -254,7 +276,13 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends (
}
}
- if (doc.docstatus == 1 && !doc.is_return && doc.status != "Closed" && flt(doc.per_billed) < 100) {
+ if (
+ doc.docstatus == 1 &&
+ !doc.is_return &&
+ doc.status != "Closed" &&
+ flt(doc.per_billed) < 100 &&
+ frappe.model.can_create("Sales Invoice")
+ ) {
// show Make Invoice button only if Delivery Note is not created from Sales Invoice
var from_sales_invoice = false;
from_sales_invoice = me.frm.doc.items.some(function (item) {
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 4b5ab3836c5..69a1bdf17d8 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -693,7 +693,8 @@ def get_items_with_location_and_quantity(item_doc, item_location_map, docstatus)
# if stock qty is zero on submitted entry, show positive remaining qty to recalculate in case of restock.
remaining_stock_qty = item_doc.qty if (docstatus == 1 and item_doc.stock_qty == 0) else item_doc.stock_qty
- while flt(remaining_stock_qty) > 0 and available_locations:
+ precision = frappe.get_precision("Pick List Item", "qty")
+ while flt(remaining_stock_qty, precision) > 0 and available_locations:
item_location = available_locations.pop(0)
item_location = frappe._dict(item_location)
@@ -838,6 +839,7 @@ def validate_picked_materials(item_code, required_qty, locations, picked_item_de
def filter_locations_by_picked_materials(locations, picked_item_details) -> list[dict]:
filterd_locations = []
+ precision = frappe.get_precision("Pick List Item", "qty")
for row in locations:
key = row.warehouse
if row.batch_no:
@@ -856,7 +858,7 @@ def filter_locations_by_picked_materials(locations, picked_item_details) -> list
if row.serial_nos:
row.serial_nos = list(set(row.serial_nos) - set(picked_item_details[key].get("serial_no")))
- if row.qty > 0:
+ if flt(row.qty, precision) > 0:
filterd_locations.append(row)
return filterd_locations
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 42747818d2b..cae58de9303 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -3,7 +3,7 @@
import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, cint, cstr, flt, nowtime, today
+from frappe.utils import add_days, cint, cstr, flt, getdate, nowtime, today
from pypika import functions as fn
import erpnext
@@ -2961,6 +2961,35 @@ class TestPurchaseReceipt(FrappeTestCase):
self.assertSequenceEqual(expected_gle, gl_entries)
frappe.local.enable_perpetual_inventory["_Test Company"] = old_perpetual_inventory
+ def test_manufacturing_and_expiry_date_for_batch(self):
+ item = make_item(
+ "_Test Manufacturing and Expiry Date For Batch",
+ {
+ "is_purchase_item": 1,
+ "is_stock_item": 1,
+ "has_batch_no": 1,
+ "create_new_batch": 1,
+ "batch_number_series": "B-MEBATCH.#####",
+ "has_expiry_date": 1,
+ "shelf_life_in_days": 5,
+ },
+ )
+
+ pr = make_purchase_receipt(
+ qty=10,
+ rate=100,
+ item_code=item.name,
+ posting_date=today(),
+ )
+
+ pr.reload()
+ self.assertTrue(pr.items[0].serial_and_batch_bundle)
+
+ batch_no = get_batch_from_bundle(pr.items[0].serial_and_batch_bundle)
+ batch = frappe.get_doc("Batch", batch_no)
+ self.assertEqual(batch.manufacturing_date, getdate(today()))
+ self.assertEqual(batch.expiry_date, getdate(add_days(today(), 5)))
+
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 87bf2df5f61..63e1e5084d6 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -1220,6 +1220,7 @@ def get_serial_batch_ledgers(item_code=None, docstatus=None, voucher_no=None, na
"`tabSerial and Batch Entry`.`warehouse`",
"`tabSerial and Batch Entry`.`batch_no`",
"`tabSerial and Batch Entry`.`serial_no`",
+ "`tabSerial and Batch Entry`.`name` as `child_row`",
]
if not child_row:
@@ -2104,10 +2105,13 @@ def get_ledgers_from_serial_batch_bundle(**kwargs) -> list[frappe._dict]:
)
for key, val in kwargs.items():
- if not val:
+ if val is None:
continue
- if key in ["get_subcontracted_item"]:
+ if not val and isinstance(val, list):
+ return []
+
+ if key == "get_subcontracted_item":
continue
if key in ["name", "item_code", "warehouse", "voucher_no", "company", "voucher_detail_no"]:
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 8301a706183..674624e184b 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -404,7 +404,9 @@ class StockReconciliation(StockController):
fields=["total_qty as qty", "avg_rate as rate"],
)[0]
+ bundle_data.qty = abs(bundle_data.qty)
self.calculate_difference_amount(item, bundle_data)
+
return True
inventory_dimensions_dict = {}
@@ -464,11 +466,16 @@ class StockReconciliation(StockController):
frappe.msgprint(_("Removed items with no change in quantity or value."))
def calculate_difference_amount(self, item, item_dict):
- self.difference_amount += flt(item.qty, item.precision("qty")) * flt(
- item.valuation_rate or item_dict.get("rate"), item.precision("valuation_rate")
- ) - flt(item_dict.get("qty"), item.precision("qty")) * flt(
- item_dict.get("rate"), item.precision("valuation_rate")
- )
+ qty_precision = item.precision("qty")
+ val_precision = item.precision("valuation_rate")
+
+ new_qty = flt(item.qty, qty_precision)
+ new_valuation_rate = flt(item.valuation_rate or item_dict.get("rate"), val_precision)
+
+ current_qty = flt(item_dict.get("qty"), qty_precision)
+ current_valuation_rate = flt(item_dict.get("rate"), val_precision)
+
+ self.difference_amount += (new_qty * new_valuation_rate) - (current_qty * current_valuation_rate)
def validate_data(self):
def _get_msg(row_num, msg):
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 8845bdbb753..48d67c2cf46 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -647,7 +647,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
"has_serial_no": 1,
"has_batch_no": 1,
"serial_no_series": "SRS9.####",
- "batch_number_series": "BNS9.####",
+ "batch_number_series": "BNS90.####",
"create_new_batch": 1,
},
)
@@ -680,7 +680,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
{
"is_stock_item": 1,
"has_batch_no": 1,
- "batch_number_series": "BNS9.####",
+ "batch_number_series": "BNS91.####",
"create_new_batch": 1,
},
).name
@@ -1109,6 +1109,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
)
sr.reload()
+ self.assertEqual(sr.difference_amount, 98900.0)
self.assertTrue(sr.items[0].current_valuation_rate)
current_sabb = sr.items[0].current_serial_and_batch_bundle
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js
index 0443f3f1ece..79638590f9b 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.js
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.js
@@ -41,7 +41,7 @@ frappe.ui.form.on("Stock Settings", {
msg += " ";
msg += __("This is considered dangerous from accounting point of view.");
msg += "
";
- msg += "Do you still want to enable negative inventory?";
+ msg += __("Do you still want to enable negative inventory?");
frappe.confirm(
msg,
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 64ad36ff5b1..27d9f1164bc 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -137,6 +137,10 @@ class StockBalanceReport:
report_data.update(
{"reserved_stock": sre_details.get((report_data.item_code, report_data.warehouse), 0.0)}
)
+
+ if report_data and report_data.bal_qty == 0 and report_data.bal_val == 0:
+ continue
+
self.data.append(report_data)
def get_item_warehouse_map(self):
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index e804ae18016..d0195843c5f 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -1794,6 +1794,7 @@ def get_next_stock_reco(kwargs):
sle.actual_qty,
sle.has_batch_no,
)
+ .force_index("item_warehouse")
.where(
(sle.item_code == kwargs.get("item_code"))
& (sle.warehouse == kwargs.get("warehouse"))
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index eca01a57640..f2388e5737a 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -276,11 +276,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True):
sn_obj = SerialNoValuation(sle=args, warehouse=args.get("warehouse"), item_code=args.get("item_code"))
return sn_obj.get_incoming_rate()
- elif (
- args.get("batch_no")
- and frappe.db.get_value("Batch", args.get("batch_no"), "use_batchwise_valuation", cache=True)
- and not args.get("serial_and_batch_bundle")
- ):
+ elif args.get("batch_no") and not args.get("serial_and_batch_bundle"):
args.actual_qty = args.qty
args.batch_nos = frappe._dict({args.batch_no: args})
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
index 81662a6257b..0f5fe7ab958 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
@@ -623,8 +623,8 @@ class TestSubcontractingReceipt(FrappeTestCase):
"has_batch_no": 1,
"has_serial_no": 1,
"create_new_batch": 1,
- "batch_number_series": "BNGS-.####",
- "serial_no_series": "BNSS-.####",
+ "batch_number_series": "BNGS0-.####",
+ "serial_no_series": "BNSS90-.####",
}
).name
@@ -715,8 +715,8 @@ class TestSubcontractingReceipt(FrappeTestCase):
"has_batch_no": 1,
"has_serial_no": 1,
"create_new_batch": 1,
- "batch_number_series": "BNGS-.####",
- "serial_no_series": "BNSS-.####",
+ "batch_number_series": "BNGS91-.####",
+ "serial_no_series": "BNSS91-.####",
}
).name
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index 3b7812f96c2..6fab5380c38 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -168,6 +168,69 @@ class TransactionBase(StatusUpdater):
if len(child_table_values) > 1:
self.set(default_field, None)
+ def validate_currency_for_receivable_payable_and_advance_account(self):
+ if self.doctype in ["Customer", "Supplier"]:
+ account_type = "Receivable" if self.doctype == "Customer" else "Payable"
+ for x in self.accounts:
+ company_default_currency = frappe.get_cached_value("Company", x.company, "default_currency")
+ receivable_payable_account_currency = None
+ advance_account_currency = None
+
+ if x.account:
+ receivable_payable_account_currency = frappe.get_cached_value(
+ "Account", x.account, "account_currency"
+ )
+
+ if x.advance_account:
+ advance_account_currency = frappe.get_cached_value(
+ "Account", x.advance_account, "account_currency"
+ )
+ if receivable_payable_account_currency and (
+ receivable_payable_account_currency != self.default_currency
+ and receivable_payable_account_currency != company_default_currency
+ ):
+ frappe.throw(
+ _(
+ "{0} Account: {1} ({2}) must be in either customer billing currency: {3} or Company default currency: {4}"
+ ).format(
+ account_type,
+ frappe.bold(x.account),
+ frappe.bold(receivable_payable_account_currency),
+ frappe.bold(self.default_currency),
+ frappe.bold(company_default_currency),
+ )
+ )
+
+ if advance_account_currency and (
+ advance_account_currency != self.default_currency
+ and advance_account_currency != company_default_currency
+ ):
+ frappe.throw(
+ _(
+ "Advance Account: {0} must be in either customer billing currency: {1} or Company default currency: {2}"
+ ).format(
+ frappe.bold(x.advance_account),
+ frappe.bold(self.default_currency),
+ frappe.bold(company_default_currency),
+ )
+ )
+
+ if (
+ receivable_payable_account_currency
+ and advance_account_currency
+ and receivable_payable_account_currency != advance_account_currency
+ ):
+ frappe.throw(
+ _(
+ "Both {0} Account: {1} and Advance Account: {2} must be of same currency for company: {3}"
+ ).format(
+ account_type,
+ frappe.bold(x.account),
+ frappe.bold(x.advance_account),
+ frappe.bold(x.company),
+ )
+ )
+
def delete_events(ref_type, ref_name):
events = (