====== Algoritam "važećeg perioda" (optmizacija) ====== Autor: Milan Oparnica (14.07.2009)\\ Revizija: Milan Oparnica (06.08.2017) Od sada zapisi koji ne trebaju imati uticaj na važeći period imaju NULL u vazod/vazdo Algoritam iza zadatak da ukloni sledeća uska grla kod određivanja prodajne i cene koštanja: - Utvrđivanje važećih cena prilikom izrade konkretnog dokumenta (na dan) - Određivanje početne vrednosti za potrebe ažuriranje cena - Utvrđivanje otpremnica i računa sa netačnim srednjim cenama i njihova ispravka ===== Određivanje početnih vrednosti za ažuriranje cena ===== ==== Standardni algoritam ==== Standardni algoritam je uvek polazio od početnog stanja i obrađivao sve promene do kraja. Postojale su dve verzije, svi artikli, ili artikli po nekom dokumentu, pri čemu ova poslednja verzija nije pokretana u slučaju da dokument obuhvata više od 300 promena. Dakle program bi uvek povukao sve promene od početka godine što u slučaju velikih baza podataka nije bilo izvodljivo. ==== Algoritam važećeg perioda ==== Algoritam se oslanja na upotrebu **LagerNaDanX** metoda za dobijanje "početnog stanja" cena i količina prilikom obrade konkretnog dokumenta ili svih artikala i skladišta. Na taj način, nema potrebe da se obrađuju hiljade slogova već samo slogovi od datuma dokumenta do kraja perioda. U slučaju da se vrši obrada svih artikala i skladišta obrada se vrši od nekih "//check-point//" tačaka do kraja perioda. ===== Utvrđivanje otpremnica i računa sa netačnim srednjim cenama i nivelacijama ===== ==== Standardni algoritam ==== Oslanja se na upit **CS_OtpremniceUlazi** koji pokušava da spoji otpremnice i ulaze sa validnim cenama za period u kom je otpremnica izdata. \\ Upitu je potrebno preko 20 min da vrati rezultat na srednjim (>300MB) i večnost na velikim bazama (>1GB). ==== Algoritam važećeg perioda ==== Uočio sam da je glavni problem u nedostatku efikasne strukture kroz koju bi lako određivao periode u kojim određena srednja ili prodajna cena važe za određeni artikal i određeno skladište. \\ Tabela **SasUlaz** se pokazala najzgodnijom za uvođenje ove strukture, koju sam nazvao "**Struktura važećeg perioda cena**", ili kraće struktura "**važećeg perioda**". ====== Realizacija strukture "važećeg perioda" ====== Strukturu čine tri polja u tabeli **SasUlaz** ^Polje^Tip^Svrha| |vazod|INTEGER|Celobrojno polje koje predstavlja **datum početka** perioda za koji srednja i prodajna cena u slogu **SasUlaz** važi.| |vazdo|INTEGER|Celobrojno polje koje predstavlja **datum kraja** perioda (ne uključujući dan unet u **VazDo**) od kog srednja i prodajna cena u slogu **SasUlaz** više ne važi.| |vazdts|DOUBLE|Duplikat **DTStamp** polja iz **Ulaz** tabele kako bi se izbegao nepotreban **JOIN** u upitima koji će vraćati rezulate strukture.| ===== Validne vrednosti polja VazOd i VazDo (logička provera) ===== Primeri su dati nad sledećim vrednostima (#m/dd/yyyy#): \\ 39875 - brojčana vrednost datuma #3/3/09# \\ 39885 - brojčana vrednost datuma #3/13/09# \\ 401769 - brojčana vrednost datuma #1/1/3000# \\ \\ Sledeće kombinacije vrednosti ovih polja za isti **ElID** i **SklID** sloga **SasUlaz** su ispravne: ^vazod^vazdo^Opis situacije| |39875|401769|Ovo je normalno kod prvog i jedinog ulaza artikla koji tada važi od datuma ulaza do kraja (1/1/3000)| | | | | |39875|39885|vo je normalno za artikal koji je ponovo nabaljen za 10 dana, pri čemu bi drugi slog izgledao...| |39885|401769|...ovako| | | | | |39875|39875|Ovo je normalno za artikal koji je u toku jednog dana više puta ulazio. Mogući scenario sledećih polja je...| |39875|39875|...ponovo je ušao, ali ima još jedan ulaz od istog dana| |39875|39885|...sledeći ulaz od istog dana važi narednih 9 dana| |39885|401769|...a 10-tog dana je artikal ponovo ušao, i od tada se više nije pojavljivao| Iz ovih vrednosti mogu se izvući sledeći logički zaključci: - Polja sa **vazod = vazdo** ne uzimaju se u obzir prilikom odabira cena jer je pravilo Balansa da svi ulazi uvek ide pre bilo kog izlaza. Izuzetak je jedino popis, koji je zadnji dokument nekog dana. Nama su dakle interesantni samo poslednji ulazi dana tj oni gde je** vazod < vazdo**. - Ni jedan par **[elid, sklid]** gde je **vazod < vazdo**ne može da ima dva ista **vazod**datuma. To bi značilo da postoji "preklapanje" perioda u okviru istorije promena cena. - Ni jedan par **[elid, sklid]** gde je ** vazod < vazdo**ne može da ima dva ista **vazod**datuma. To bi značilo da postoji "preklapanje" perioda u okviru istorije promena cena. - Ni jedan par **[elid, sklid]** ne može da ima **vazod**ili **vazdo**između **vazod**i **vazdo** (**>** ** vazodAND < vazdo**) bilo kojeg drugog para istih**[elid, sklid]**. \\ **Napomena ****!** Upit koji bi ovo proveravao ne postoji, ali postoji pretpostavka da će takva situacija uvek dovesti do pojave duplikata u **vazod**ili **vazdo**. ====== Popisi ====== Izuzetno je važno da se popisi više ne tretiraju kao poslednji dokumenti u danu, ako već nisu tako i snimljeni. Periodi **vazod** i **vazdo** ne smeju postati netačni. Ovo tretiranje popisi imaju samo u procedurama za obradu srednjih cena i nivelacija, i to zato što svi ulazi pomeraju **DTStamp** svih otpremnica tako da ove **uvek** dolaze iza njih. Popisima se na **DTStamp** dodaje **20000** tako da se oni veštački postave iza svih ulaznih dokumenata. Ovo je prvenstveno urađeno zbog uprošćavanja detekcije trenutnih količina u trenutku ulaza i formiranja srednje nabavne cene. Pošto su svi izlazi agregirani pod jednim danom i smešteni na njegov kraj, jedini podatak koji nam informaciju može pružiti jeste **TKol** **prethodnog** **ulaza**. Stvar, nažalost, nije nimalo jednostavna. **Ulazi** pre popisa će u **TKol** da smeste sve ulaze do tog momenta minus količinu robe prodatu do kraja prethodnog dana. **Popis** će uzeti stanje u trenutku popisa i u svoj **TKol** upisati stanje posle popisa uključujući izlaze do momenta popisa. \\ Ulazi napravljeni posle popisa će uzeti: zbir svih ulaza do pre popisa + izmene po popisu – zbir izlaza do kraja prošlog dana. Tablica koja demonstrira problem: {{:kb:artikli:ce1e8632f0242f42df5b0b9cfd9d31c5.png}} Iz ovoga se vidi da će prethodni **TKol** biti isti kao i stanje lagera po obračunu **SC** ali će se **razlikovati** od stvarnog stanja koje je bilo u trenutku popisa. ===== Rešenje ===== Razlog za usaglašavanje dokumenta Popis može biti samo promena u stanju (ili ceni, ali ako to nema veze sa količinom radi se o drugoj priči) pre popisa. Do promene stanja može doći samo izmenom tabele **Ulaz** ili **Otpremnice**. U prvom slučaju, izmena će se odraziti na **TKol** makar poslednjeg **Ulaza** pre popisa. U drugom slučaju desiće se isto sem ako je došlo do promene baš one otpremnice koja se nalazila između poslednjeg ulaza i popisa, odnosno popisa i prvog sledećeg ulaza, a to će se reflektovati tek na **TKol**-u prvog sledećeg Ulaza. Problem je kako razlikovati da li je do razlike u izlazima došlo pre ili posle popisa ? Jedino rešenje je ipak da popis u okviru zapisa u **Popis** unese i podatak o** ukupnom ulazu i izlazu** do trenutka izrade popisa. U tu svrhu su dodata polja **inkol** i **outkol**. Prilikom sakupljanja informacija o prometu artikala (**CS_UlaziSumIzlazi** i sl.), dodat je podupit koji za slogove popisa testira trenutno važeći izlaz do momenta popisa i posle popisa (potonje za sada ne trebaju, ali pošto ne utiču na performanse ostavio sam ih). ==== Implementacija u BLSCNIV biblioteci ==== U strukturu **uMag** dodaću dva nova polja **InKol** i **OutKol**, ali i u matricu popisa isto to. Slog u **SasUlaz** će u polju **ExtLink** pamtiti **PopID** polje iz tabele **Popis**. === Matrica popisa === Matrica popisa se formira prilikom prebacivanja podataka iz recordseta sa prometom artikala. Tada se za sve slogove, za koje se otkrije da predstavljaju slog popisa, umetne zapis u matricu popisa koja ima sledeći format: ^Naziv polja^Vrsta^Opis| |iIndex|Long|Ovde se upisuje broj reda iz osnovne matrice gde se čuvaju svi podaci o slogu popisa. Pošto je taj broj unikatan poslužiće nam kao unikatan index.| |iElID|Long|ID artikla na koji se popis odnosi| |iSklID|Long|ID skladišta na kom je popis urađen| |dDatum|TDateTime|Datum i vreme izrade popisa| |nLoTKol|Double|Originalna TKol vrednost učitana iz baze| |nLoKol|Double|Količina uneta kroz popis| |nChTKol1|Double|Vrednost TKol-a iz poslednjeg Ulaza pre popisa| |nChTKol2|Double|Vrednost TKol-a iz Ulaza posle popisa| | | |//< ima još polja u ovoj matrici...potrebno je updateovati ovaj dokument >// | === Implementacija === Izmene se dešavaju u modulu za obračun srednjih cena i nivelacija [[:l2:rob:b3:blscniv:index_main|BLSCNIV]]. \\ Koraci bitni za implementaciju: - Sve izmene uMag.TKol (trenutna količina po faktičkom stanju) su preusmereni na jednu proceduru mSetNewTKol. \\ Izuzetak je jedino promena koja se dešava pod uicajem izlaza (Otpremnice) jer ona ne podleže analizi. - TUlazRec strukturi dodato je novo polje LastUlDocTip. U to polje se smešta tip ulaznog dokumenta u trenutku izmene/potvrde TKol-a, kako bi u narednj izmeni mogao da posluži kao informacija o prethodnom tipu dokumenta. \\ Suština ovog polja je da ukaže da li je pre konkretnog Ulaza bio dokument Popisa. Tu je samo u cilju optimizacije, tj. da ne bi svaka izmena morala da traži unazad koji joj je bio prethodni dokument. Pošto nas samo zanima da li je to bio Popis, moguće je da se u ovom polju pojavi i 0. Otpremnice neće uticati na ovo polje. \\ Problem predstavljaju Ulazi po osnovu premeštaja. Tada se ne preduzima ništa, već se nit tekućeg obračuna prekida. Pošto će u budućnosti nit biti nastavljena pozivom mGetNeobradjen funkcije, logično je da ta funkcija postavi i koji je to dokument prethodio ovom koji će se obrađivati (za sve pozive, ne samo za ovaj slučaj). Pošto se u okviru ove funkcije poziva i mGetPrevMCen kako bi se utvrdila prethodna prodajna cena, proširio sam tu funkcija da setuje mbLastPrevDocTip u prvi prethodni DocTip. Vrednost mbLastPrevDocTip mora da se pročita odmah po izlasku iz mGetPrevMCen funkcije. ====== Izuzetci iz strukture "važećeg perioda" ====== ===== Inetrni nalozi za prijem i prenos robe ===== U strukturu se ne unose slogovi iz **SasUlaz** koji imaju **sklid = 0**. U Balansu jedino interni nalozi za prijem ili prenos robe snimaju u **Ulaz > SasUlaz** pod **sklid = 0**. \\ Pošto ti podaci ionako ne učestvuju u formiranju cena i količina sasvim je ispravno da ne koriste strukturu važećeg perioda. ===== Dokumenti koji ne utiču na obračun prodajne ili cene koštanja ===== Od **06.08.2017** zapisi koji ne trebaju imati uticaj na važeći period (''defDocTip.FNivelTot = false i defDocTip.FSCObrac = false'') imaju **NULL** u **vazod**/**vazdo** ====== Važni upiti ====== ^Upit^Opis| |CS_UlaziDDDoc|Novi upit za dobijanje obracunskog peroda sc i nivel po predatom dokumentu| |CS_UlaziSumIzlazi_Tst|CS_UlaziSumIzlazi samo bez UNIONA po Ulaz tabelama| |zCalc_INKOLByDoc_xx|Upiti za dobijanje stanja svih lagera artikala predatog dokumenta na trenutak pre izrade predatog dokumenta| |CS2_GetPer4Del| | |CS2_GetPer4New| | |CS2_GetPerErr_1| | \\