Servis: self-heal pri otvaranju detalja — povuci dostupno stanje u potraživane
Pri otvaranju detalja naloga, ako za potraživani artikal ima stanja na magacinu, automatski se povlači (delimično ili u celosti) i skida sa lagera. Hvata sve načine na koje stanje poraste (i brisanje dela, povraćaj prodaje), ne samo nabavku/izmenu/popis. Dodat test za delimično pokrivanje.
This commit is contained in:
@@ -74,6 +74,56 @@ func TestPotrazivaniTok(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPotrazivaniDelimicnoPokrivanje: stanje (1) manje od potraživanog (3) —
|
||||||
|
// povuče se samo dostupno, ostatak ostaje na čekanju, nalog ostaje zaključan.
|
||||||
|
func TestPotrazivaniDelimicnoPokrivanje(t *testing.T) {
|
||||||
|
db := testDB(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if _, err := db.ExecContext(ctx, "INSERT INTO artikli (id, naziv, kolicina) VALUES (1, 'Test CPU', 0)"); err != nil {
|
||||||
|
t.Fatalf("insert artikal: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := db.ExecContext(ctx,
|
||||||
|
"INSERT INTO servisni_nalozi (id, broj_naloga, uredjaj, opis_kvara, status) VALUES (1, 'SN-1', 'PC', 'kvar', 'Čeka delove')",
|
||||||
|
); err != nil {
|
||||||
|
t.Fatalf("insert nalog: %v", err)
|
||||||
|
}
|
||||||
|
// ugrađeno 2, potraživano 3 (kao da je traženo 5 a bilo 2 na stanju)
|
||||||
|
if _, err := db.ExecContext(ctx, "INSERT INTO servisni_delovi (nalog_id, artikal_id, kolicina, cena_komada) VALUES (1, 1, 2, 100)"); err != nil {
|
||||||
|
t.Fatalf("insert deo: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := db.ExecContext(ctx, "INSERT INTO servisni_potrazivani_delovi (nalog_id, artikal_id, kolicina, cena_komada) VALUES (1, 1, 3, 100)"); err != nil {
|
||||||
|
t.Fatalf("insert potraživani: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stigao samo 1 komad
|
||||||
|
if _, err := db.ExecContext(ctx, "UPDATE artikli SET kolicina = 1 WHERE id=1"); err != nil {
|
||||||
|
t.Fatalf("dopuna: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
potrRepo := NoviServisniPotrazivaniDeloviRepo(db)
|
||||||
|
otkljucani, err := potrRepo.ProveriIPocistiZaArtikal(ctx, 1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ProveriIPocistiZaArtikal: %v", err)
|
||||||
|
}
|
||||||
|
// nalog se NE otključava jer još fali 2
|
||||||
|
if len(otkljucani) != 0 {
|
||||||
|
t.Fatalf("nalog ne sme da se otključa, dobijeno %v", otkljucani)
|
||||||
|
}
|
||||||
|
// ugrađeno: 2 + 1 = 3
|
||||||
|
if k := skalarInt(t, db, "SELECT kolicina FROM servisni_delovi WHERE nalog_id=1 AND artikal_id=1"); k != 3 {
|
||||||
|
t.Fatalf("ugrađeni deo očekivano 3, dobijeno %d", k)
|
||||||
|
}
|
||||||
|
// potraživano: 3 − 1 = 2
|
||||||
|
if k := skalarInt(t, db, "SELECT kolicina FROM servisni_potrazivani_delovi WHERE nalog_id=1 AND artikal_id=1"); k != 2 {
|
||||||
|
t.Fatalf("potraživani očekivano 2, dobijeno %d", k)
|
||||||
|
}
|
||||||
|
// stanje magacina: 1 − 1 = 0
|
||||||
|
if k := skalarInt(t, db, "SELECT kolicina FROM artikli WHERE id=1"); k != 0 {
|
||||||
|
t.Fatalf("stanje očekivano 0, dobijeno %d", k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// skalarInt vraća jednu celobrojnu vrednost iz upita (za proveru stanja u testu)
|
// skalarInt vraća jednu celobrojnu vrednost iz upita (za proveru stanja u testu)
|
||||||
func skalarInt(t *testing.T, db *sql.DB, upit string) int {
|
func skalarInt(t *testing.T, db *sql.DB, upit string) int {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|||||||
@@ -403,6 +403,33 @@ func (h *Handler) DetaljiNaloga(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// self-heal: ako u međuvremenu ima stanja na magacinu za potraživane artikle,
|
||||||
|
// povuci ih (delimično ili u celosti) i otključaj nalog ako je sve pokriveno.
|
||||||
|
// Hvata sve načine na koje stanje poraste, ne samo nabavku/izmenu/popis.
|
||||||
|
if potrazivani, e := h.ServisniPotrazivaniDeloviRepo.DohvatiZaNalog(r.Context(), id); e == nil && len(potrazivani) > 0 {
|
||||||
|
vidjeni := map[int64]bool{}
|
||||||
|
for _, p := range potrazivani {
|
||||||
|
if vidjeni[p.ArtikalID] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vidjeni[p.ArtikalID] = true
|
||||||
|
otkljucani, err := h.ServisniPotrazivaniDeloviRepo.ProveriIPocistiZaArtikal(r.Context(), p.ArtikalID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("self-heal potraživanih delova nije uspeo", "artikal_id", p.ArtikalID, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, nalogID := range otkljucani {
|
||||||
|
if err := h.ServisRepo.AzurirajStatus(r.Context(), nalogID, model.StatusPrimljeno); err != nil {
|
||||||
|
slog.Error("self-heal reset statusa naloga nije uspeo", "nalog_id", nalogID, "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// status naloga se možda promenio — ponovo ga učitaj za prikaz
|
||||||
|
if osvezen, e := h.ServisRepo.DohvatiID(r.Context(), id); e == nil && osvezen != nil {
|
||||||
|
nalog = osvezen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
podesavanja, err := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
|
podesavanja, err := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
|
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
|
||||||
|
|||||||
Reference in New Issue
Block a user