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 = (