MRP’de Fraksiyonlu Ürün Planlama
Öncelikle bu blog yazısına konu olan fraksiyonlu ürünlerin ne anlama geldiğiyle konuya giriş yapacağım.
Öncelikle bu blog yazısına konu olan fraksiyonlu ürünlerin ne anlama geldiğiyle konuya giriş yapacağım. SPRO Teknoloji şirketinde MRP projesi yapılan örneği, yağ üreten bir firma ile gerçekleştirildi. Fraksiyon aslında bölünmek anlamına geliyor. Bu şirkette kullanım yeri ise, yarı mamul seviyesindeki bazı yağlar üretilirken, fraksiyonu olan başka bir malzemenin de açığa çıkıyor olması. Yani tıpkı bir eş ürün gibi ortaya çıkıyor. Ancak bu projenin yapılmasına sebep olan ihtiyaç aslında bu ürünlerin belli bir orana göre ortaya çıkıyor olması ve MRP çalışırken bu orana göre planlama yapılmasının istenmesi.
Tarafımıza bildirilen ihtiyacın karşılanabilmesi için, öncelikle MRP bir malzemeye planlama yaparken, fraksiyonlu ürününe de planlama yapmasını sağlayabilmek için, bir geliştirmeyle müdahale etmek gerekiyordu. Dolayısıyla ilk iş olarak bu geliştirmenin nereye yapılacağını bulmak için MRP çalışırken arkada çalışan kodu debugladık. Debuglama işlemi sonrasında “MM61XMDV_READ_NEXT_MDMMX_PL” isimli include’un bu işlem için uygun olduğuna karar verdik.
MARA tablosunda malzemelerin, teknik ismi DISST olan bir değeri bulunmakta. Tanımı “MİP Düzeyi” olarak geçmektedir. Bu değer, MRP çalışırken malzemelerin hangi sırayla planlanacağını belirlemek için kullanılıyor. Bulduğumuz include’a malzemeler, MDMMX adlı tablo içerisinde, DISST’lerine göre düşüyor. Bulunan include’un başında bulunan enhancement spot’a yazılacağı belirleniyor. Bu bilgiler edinildikten sonra geliştirmenin nereye yapılacağı kesinleşmiş oluyor.
Yazının başında bahsettiğimiz gibi, fraksiyonlu ürün bir orana göre ortaya çıktıkları için, bu oranların tutulacağı bir tabloya ihtiyaç doğuyor. Bu sebepten, oran verilerinin tutulabilmesi için bir bakım tablosu oluşturuldu ve bu tablonun içi dolduruldu. Tablonun son görüntüsü aşağıdaki gibi oldu:
Tabi kullanıcıların bu verileri güncel tutmaları gerektiği ve yeni malzeme eklenme gibi durumlarda tablonun bakımının yapılması gerektiği için, bakım tablosuna bir işlem kodu tanımlandı. Bu işlem koduyla kullanıcılar, gerekli gördükleri durumlarda tablonun bakımını yapabilir duruma geldiler.
Gelelim kodun enhancement yaptığımız kısmına. MRP çalışır çalışmaz ilk defa enhancement’a düştüğünde öncelikle, yaptığımız geliştirme vasıtasıyla oluşturulan planlı siparişlerini silmek için, silme işleminin yapılıp yapılmadığını anlamak amacıyla tanımlanan delete_run değişkeni import edilerek değerine bakılır. Eğer delete_run boş ise, önceki planlamalarda oluşturulan planlı siparişlerin silinmesi işlemi için yazılan koşula girecektir. Bu, silme işleminin daha yapılmadığı anlamına gelmektedir.
Bu koşulun içine girdikten sonra kontrol edilecek şey ise planlama senaryosunun (PLSCN alanı) dolu olup olmadığıdır. Çünkü eğer planlama senaryosu doldurulduysa, tablolara planlama senaryosu bilgisi dolu olarak kaydedilmektedir. Silmek için kayıtlar veri tabanında aranırken de bu bilgiyle aranması gerekmektedir.
Bu bloğa konu olan geliştirme vasıtasıyla oluşturulacak olan planlı siparişleri ayırt edebilmek için, “ZA” kodlu bir planlı sipariş profili (PASCH) oluşturulmuştur. Ancak bu sipariş profili de planlı siparişler bapi yardımıyla oluşturulurken PASCH alanına yazılamadığından, geliştirmenin yapıldığı sistemde kullanılmıyor olan “Sabit Satıcı” (FLIEF) alanına yazılmıştır. Dolayısıyla da silinmesi istenen planlı siparişler veri tabanından çekilirken, planlama senaryosu (dolu ise) ve sabit satıcısı “ZA” olanlar şeklinde PLAF tablosundan çekilmiştir ve gt_plaf adında bir internal tabloya doldurulmuştur.
Silinmesi istenen planlı siparişler çekildikten sonra, “BAPI_PLANNEDORDER_DELETE” bapisine loop içinde planlı sipariş numaraları (PLNUM) verilerek veri tabanından silinmiştir. Silme işlemi yapıldıktan sonra ise, kod bir daha bu kısma düştüğünde silme işleminin yapıldığını anlayabilmek için ilk başta kontrol edilen “delete_run” değişkeni X’lenir ve memory id’ye import edilir.
Sonrasında “IF rm61x-werks = ‘0191’ AND mdmmx-disst = ‘999’.” kontrolü yapılır.
Bu koşulun olmasının sebebi, MRP kendi planlamasını bitirdikten sonra en son DISST’si 999 olanları işleyecektir. Planlamada en düşük öneme sahip kısım bu olduğu için 999 olanları işlemeden hemen önce geliştirmeyi yapıyoruz.
Sonrasında fraksiyon oran tablosuna select count atılır ve subrc 0 döndüyse kod devam eder. Burada tablonun dolu olup olmadığı kontrol edilir.
Sonrasında gv_first_run ve gv_oncelik ismindeki iki farklı değişken import edilir. Bu şekilde 2 ayrı değişkenimiz olmasının sebebi, bu kısma ilk kez düştüğümüzde yaptığımız işlemlerin ayrılması, ve planlanacak fraksiyonlu malzemelerin kendi içinde bir önem sırasına sahip olmasıdır.
Eğer gv_first_run boş ise, yani bu kısma ilk kez düşülüyor ise, bu sefer fraksiyon oranları bir internal tabloya doldurulur. Fraksiyon oran tablosunda, malzemelerin birbirine oranları mevcut olduğu için, bir satırda 2 malzeme bulunmaktadır. Her malzemeyi tek satır şeklinde yazmak için bu malzemeler loop’ta dönerek ayrı bir internal tabloya tekrar doldurulur ve küçükten büyüğe doğru sıralanır.
Sıralandıktan sonra, bu tablo içindeki tüm malzemeler için CS_BOM_EXPL_MAT_V2 fonksiyonu ile ürün ağacı patlatılır. Ürün ağaçları patlatıldıktan sonra, tüm bileşenler, tepe malzemeleri ve ürün ağacındaki kademeleriyle birlikte ayrı bir internal tabloya doldurulur. Sonrasında, bu tablo, IDNRK (yani bileşen) alanına, fraksiyon malzemelerinin bulunduğu tablodaki malzemeler verilerek okunur, eğer bu malzemeler bileşen olarak bulunamaz ise, en tepedeki malzeme oldukları için öncelik değeri “1” olarak doldurulur. Eğer bileşen alanında bulunurlar ise, bulundukları bileşenin stufe (kademe) değeri lv_matnr1_stufe ve lv_matnr2_stufe adında iki farklı değişkenden ilgili olana yazılır. Sonrasında bu değişkenler kendi içlerinde karşılaştırılır. Tablonun ilgili satırındaki malzemelerin ikisinin de değişkeni dolu ise, büyük olanın kademesine 1 eklenerek ilgili kaydın öncelik alanına yazılır. İkisinden biri doluysa, dolu olanın değerine 1 eklenerek satırın öncelik alanına yazılır. Bu işlem tablodaki tüm kayıtlar işlenene dek devam eder.
Tüm kayıtlar işlenerek öncelik değerleri belirlendikten sonra bu kayıtlar bir Z’li tabloya atılır. (Tablo alanları: MATNR1, ORAN1, MATNR2, ORAN2, ONCELIK) Ardından gv_first_run değişkeninin değeri “X”, gv_oncelik değişkeninin değeri “1” yapılarak iki ayrı memory id’ye export edilir.
Bu yapılanlar gv_first_run boş ise, yani ilk kez düşülüyor ise yapılan işlerdi. Eğer ilk kez düşülmüyorsa, yani memory ID ile okuduğumuz gv_first_run değişkeninin değeri X’li ise, gv_oncelik değişkeninin değeri 1 arttırılır ve devam edilir.
Bir sonraki aşama, MRP tarafından planlaması yapılıyor olan fraksiyon malzemelerinin, güncel ihtiyaç/stok listesi (MD04) verilerinin okunması ve fraksiyonlu ürün planlaması için okunan verilerden ihtiyaç duyulanların ayıklanmasıdır. Bu verilerin okunabilmesi için “MD_STOCK_REQUIREMENTS_LIST_API” fonksiyonu kullanılmaktadır. Fonksiyonu çalıştırmak için MATNR, WERKS alanları doldurulur. Ancak MRP çalıştırılırken planlama senaryosu girildiyse (uzun dönem planlama yapılıyorsa) PLSCN alanı da doldurulur.
Fonksiyon çalıştırıldıktan sonra fonksiyondan export tablosu olarak dönen MDPSX’in içinde PLUMI değeri “+” olmayan kayıtlar silinir. PLUMI değeri + olan kayıtlar stoka giren/girecek (planlı sipariş, proses siparişi, vs.), – olanlar ise stoktan çıkacak (ihtiyaç) kayıtlardır. Fraksiyonlu ürün planlamasında dikkate alınacak kayıtlar, stoka girecek kayıtlar olacağından + olmayan kayıtlar silinmiştir. Sonrasında ise, eğer uzun dönemli planlamaysa (planlama senaryosu doluysa) MDPSX içinde DELKZ (Mip Öğesi Göstergesi) alanı SA olmayan kayıtlar, uzun dönemli planlama değilse (planlama senaryosu boşsa) DELKZ alanı PA ve BR olmayan kayıtlar silinir. Bu kayıtlarda SA “Simülasyon Siparişi”, PA “Planlı Sipariş”, BR ise “Proses Siparişi” anlamına gelmektedir.
MDPSX tablosunda sadece fraksiyonlu ürün planlamasında dikkate alınacak kayıtlar bırakıldıktan sonra, geliştirmenin yapıldığı şirkette hafta bazlı üretim planlaması yapıldığı için kayıtların hafta bilgisine ihtiyaç duyulmaktadır. Bu bilgiyi edebilmek içinse geliştirmede “GET_WEEK_INFO_BASED_ON_DATE” fonksiyonu kullanılmıştır. Bu fonksiyona hafta bilgisini öğrenme tarihi girildiğinde, ilgili tarihin yılın hangi haftasında olduğu bilgisini vermektedir. Yapılan geliştirmenin bu kısmında, MDPSX tablosunda bırakılan kayıtların DAT00 (tarih) alanı teker teker bu fonksiyonun “DATE” alanına verilerek hafta bilgileri, hesaplamalarda kullanılmak için oluşturulmuş wa_calculate structure’ına, diğer alanlarla (malzeme, planlı sipariş, vs.) birlikte yazılır.
Hesaplamalarda, tüm verilerin bir internal tabloya doldurularak, bu tablo üzerinden verilerin karşılaştırılmasıyla hesaplama yapılması şeklinde bir mantık yürütülmüştür. Bu hesaplama tablosunda, sistem de aynı şekilde değerlendirdiği için proses ve sabitlenmiş planlı siparişler aynı statüde değerlendiriliyor. Tablodaki alanlar üretim yeri, planlama senaryosu (eğer yoksa boş bırakılır), malzeme, hafta, proses + sabit planlı sipariş, planlı sipariş, toplam üretim, önceki haftalardan gelenler, toplam miktar, eş hesaplama, ekleme şeklindedir.
Bu alanlardan üretim yeri, planlama senaryosu ve malzeme, o anda işleniyor olan malzemeden ve planlama parametrelerinden standart olarak gelmektedir. “Proses Siparişi + Sabit Planlı Sipariş” alanı, hafta bazlı olarak MDPSX tablosunda, DELKZ’si BR olan ve PA olup FIX01 (Sabitleme Göstergesi) alanı X olan kayıtların MNG01 (Miktar) değerlerinin toplanarak yazılmasıyla elde edilir. “Planlı Sipariş” alanı, MDPSX tablosunda DELKZ’si PA olan ve FIX01 alanı boş olan kayıtların MNG01 değerlerinin toplanarak yazılmasıyla elde edilir.
“Toplam Üretim” alanı, “Proses Siparişi + Sabit Planlı Sipariş” alanıyla “Planlı Sipariş” alanındaki değerlerin toplanmasıyla elde edilir.
“Önceki Haftalardan Gelenler” alanının değerini bulmak için öncelike bir önceki kayıttaki “Toplam Üretim” alanının değerine bakılır. Eğer “Toplam Üretim” değeri 0 ise, bir önceki kayıttaki “Ekleme” değeri + o kaydın “Önceki Haftalardan Gelenler” alanındaki değerler toplanarak yazılır. Ancak “Toplam Üretim” alanı 0 değil ise, sadece bir önceki kayıttaki “Ekleme” değeri yazılır. Fakat algoritmada, işleniyor olan satırdaki ekleme değeri hesaplandıktan sonra, sonraki ilgili kaydın önceki haftalardan gelen alanına yazılır.
“Toplam Miktar” alanına, o satırdaki “Önceki Haftalardan Gelen” değeri “Toplam Üretim” değerinden büyükse direk “Proses + Sabit Planlı Sipariş” değeri yazılarak, eğer değilse, “Planlı Sipariş” ile “Proses + Sabit Planlı Sipariş” alanını toplayıp, “Önceki Haftalardan Gelen” değeri çıkartılarak bulunur.
“Eş Hesaplama” alanı, o anda işleniyor olan malzemenin, fraksiyon oran tablosundaki oranlara göre eş ürünüyle kıyaslanarak planlanması gereken miktarı belirtir. Bu alanın değeri, “Toplam Miktar” sütununda yazan değerin fraksiyon tablosundaki kendi oranıyla çarpılıp, eşinin oranına bölünmesiyle hesaplanır.
Son olarak “Ekleme” alanının değerini bulmak için, “Eş Hesaplama” alanının “Toplam Miktar” alanından büyük olup olmadığına bakılır. Eğer büyük ise, “Eş Hesaplama” alanından “Proses + Sabit Planlı Sipariş” değeri çıkartılarak “Ekleme” alanına yazılır. Eğer küçük ise, zaten eklenecek bir miktar olmadığı için “0” yazılır. Burada alanların nasıl hesaplandığını anlatmış olduk, ancak daha rahat görülebilmesi için aşağıda alan alan tekrar yazalım.
Üretim Yeri: Standart
Planlama Senaryosu: Standart
Malzeme: Standart
Hafta: Fonksiyondan Dönen Hafta Değeri
Proses Siparişi + Sabit Planlı Sipariş: MDPSX tablosundaki DELKZ’si BR olan ve PA olup FIX01’i X olan kayıtların MNG01’lerinin toplamı
Planlı Sipariş: MDPSX tablosundaki DELKZ’si PA olup FIX01’i boş olan kayıtların MNG01’lerinin toplamı
Toplam Üretim: “Proses Siparişi + Sabit Planlı Sipariş” alanıyla “Planlı Sipariş” alanının toplanması
Önceki Haftalardan Gelen: Aynı malzemenin bir önceki haftadaki “Ekleme” alanının değerine eşittir
Toplam Miktar: “Önceki Haftalardan Gelen” > “Planlı Sipariş” ise “Proses Sipariş + Sabit Planlı Sipariş”, değil ise “Planlı Sipariş” + “Proses Siparişi + Sabit Planlı Sipariş” – “Önceki Haftalardan Gelen”
Eş Hesaplama: İşlenen kayıttaki “Toplam Miktar” değeri * Malzemenin fraksiyon tablosundaki oranı / Eş’inin fraksiyon tablosundaki oranı
Ekleme: “Eş Hesaplama” değeri “Toplam Miktar” değerinden büyük ise = “Eş Hesaplama” – “Proses Siparişi + Sabit Planlı Sipariş”. Küçük ise = “0”.
Bu hesaplama şekillerine göre kayıtlar tek tek kendiyle ilişkili satırlarda dönerek hesaplanır. Hesaplamalar sonucunda ise, ortaya aşağıdaki görseldeki gibi bir tablo çıkar.
Yukarıdaki tablo ortaya çıktıktan sonra, hangi malzemelerden hangi haftalara hangi miktarlarda tedarik önerisi oluşturulması gerektiği de ortaya çıkmış oluyor. Bu veriler elde edildikten sonra yapılması gereken, bu verilerin MRP çalıştığı sırada hangi malzemelerin hangi sırayla planlanacağı bilgisini içeren tabloya eklenmesi.
Bu aşamada, “Ekleme” alanı 0’dan büyük olan (yani ek olarak tedarik önerisi oluşturulması gereken) kayıtların ve bu kayıtların bileşenlerinin, MDMMX tablosuna eklenmesi gerekmektedir. Bu kayıtların işlenmesi için “Ekleme” alanı 0’dan büyük olan kayıtlar için bir loop oluşturulur. Bu loopun içinde, ilgili işlemi yapabilmek için, veriler bir tane internal tabloya doldurulur.
Sonrasında, “Ekleme” alanı 0’dan büyük olan kayıtlara planlı sipariş oluşturulması için BAPI_PLANNEDORDER_CREATE fonksiyonu çağrılacaktır. Ancak öncelikle, hesaplama tablosunda hafta bilgisi bulunan ve planlı sipariş oluşturulacak kayıtların, ilgili haftalarının ilk gününün tarihinin bulunması için WEEK_GET_FIRST_DAY fonksiyonu çağrılır. Hafta bilgisi fonksiyona import parametresi olarak verilir ve export olarak dönen tarih, BAPI_PLANNEDORDER_CREATE fonksiyonunun import structure’ındaki order_start_date (planlı sipariş başlangıç tarihi) alanına verilir.
Ardından BAPI_PLANNEDORDER_CREATE fonksiyonunun import parametreleri , loop ile dönülen tablonun ilgili kaydındaki verilerle, yani var ise planlama senaryosu, planlı sipariş profili, sabit satıcı, malzeme, üretim yapan üretim yeri alanları mevcut verilerle doldurulur. Planlı sipariş miktarı ise “Ekleme” alanında yazan değerler ile doldurulur. “Dönüştürme” ve “Sabitleme” indikatörleri X’lenir ve fonksiyon çalıştırılır. Ardından “return” tablosundaki veriler “return_type = ‘S’”, “return_id = ‘61’” ve “return_number = ‘010’” şeklinde döndüyse, commit için BAPI_TRANSACTION_COMMIT çağrılarak commitlenir. Veriler bu şekilde dönmediyse rollback için BAPI_TRANSACTION_ROLLBACK fonksiyonuyla rollback yapılır. “Ekleme” değeri 0’dan büyük olan tüm kayıtlar için bu işlemler döngü halinde tekrar eder.
Ardından MDMMX tablosu için doldurulmuş olan internal tablo doluysa eğer, bu internal tablonun içindeki veriler (malzeme ve bileşenleri) için MARA tablosuna gidilir ve DISST değeri alınır. Blog yazısının başında da bahsettiğim gibi bu alan, MRP’nin malzemeyi hangi sırada planlayacağını belirleyen alandır. Tüm malzemelerin DISST’si alınıp internal tabloya yazıldıktan sonra, bu tablonun içindeki veriler toplu bir şekilde MDMMX tablosuna atılır ve DISST değerine göre küçükten büyüğe doğru sıralanır.
Son olarak hesaplama tablosunun son hali, planlama çalışmasında geliştirmenin yaptığı müdahalenin incelenebilmesi için oluşturulmuş olan log tablosuna tablo şeklinde atılır. Bu işlem sonrasında algoritmanın işi de tamamlanmış olur.