Sekwencyjny numer faktury restartowany z nowym rokiem

0

Witam!

Chce napisac sekwencyjny numer faktury. Zaczynajacy sie od 0000001, 0000002, 0000003....999999. Numer faktury musi restarotowac sie do wartosci 0000001 z kazdym nowym rokiem. Niby proste, ale tez ciekawy problem.

Do tego musze dodac rok przed numerem faktury 2022-0000001 itd. W tabeli invoices mam kolumne paidDate i na niej bede opieral rok, wiec nie zapusuje ponownie roku, normalizacja. Chce zapisac wylacznie sekwenje w osobnej kolumnie.

Zastanawiam sie czy robic w ogole to na poziomie DB czy dac w BE. Jestem w stanie przetestowac oba rozwiazania z unit/integration tests.

2

Ja z latami przestałem sobie umieć wyobrazić podobnych dokumentów jak faktura, z jedną jedyną warstwą bazy danych, jak za przeproszeniem w borlandach

A skoro mamy jakieś warstwy obiektowo-relacyjne, to nie przeszkadza mi z kodu aplikacyjnego - a i to bardzo późno, w ostatniej chwili podczas zapisu.
Jakby ktoś porzucił fv numer się nie zmarnuje.

Z warstwą jedynie bazodanową nie wyobrażam sobie nie "marnować" numerów

Liczne dodatkowe argumenty:
a) klient zgodnie z prawem będzie chciał odmienną numerację, np też legalną miesięczno/roczną
b) korekcyjne, zaliczkowe
c) przybędzie dokumentów ... horror, zamówienia w innej numeracji niz fv ... jak to rozwiążesz ?

Dla mnie baza danych (w sensie: sama baza danych) nie jest właściwym miejscem do tego

4

tabela numery z polami rok, typ, numer oraz prosta procedura, która dla podanego roku i typu robi następujące rzeczy:

  1. zakłada locka na wierszu z danym rokiem i typem
  2. odczytuje bieżący numer z z tego wiersza
  3. zwiększa go o jeden
  4. aktualizuje wiersz
  5. zdejmuje locka
  6. w logice musisz jeszcze uwzględnić przypadek, kiedy rekord nie istnieje

Pobieranie numeru robisz w transakcji razem z zapisem dokumentu i nie ma siły, żebyś miał dziury w numeracji albo zdublowane numery jak czegoś nie skopiesz.

3
poniatowski napisał(a):

Do tego musze dodac rok przed numerem faktury 2022-0000001 itd. W tabeli invoices mam kolumne paidDate i na niej bede opieral rok, wiec nie zapusuje ponownie roku, normalizacja. Chce zapisac wylacznie sekwenje w osobnej kolumnie. >

Akurat zapisywanie numeru formatowanego w osobnej kolumnie moim zdaniem jest warte rozważenia. A co jak klient w roku 2022 chce mieć format FV/2022/00001 natomiast w roku 2023 zechce jeszcze łamać przez miesiąc postaci FV/2022/01/00001 albo w ogóle zmieni numerowanie z rocznego na miesięczne? Nie ogarniesz tego bez zapisywania numeru formatowanego. Trzymanie się za wszelką cenę takich reguł jest czasami ryzykowne ;) Co więcej, raczej numerację powinieneś brać według daty wystawienia dokumentu.

poniatowski napisał(a):

Zastanawiam sie czy robic w ogole to na poziomie DB czy dac w BE. Jestem w stanie przetestowac oba rozwiazania z unit/integration tests.

Ja bym radził zabezpieczyć się na bazie. Błąd w systemie może zawsze się zawsze zdarzyć, jak założysz jakiś klucz unikalny na odpowiednie pola będziesz miał 100% pewności. Chyba nie chcesz by w świat klient puścił dwie różne faktury o tym samym numerze? Widziałem takie przypadki, a wtedy robi się niezbyt miło...

0

Na razie napisales na szybkosci cos takiego. Ma to jakis sense? Prosze o code review :)


BEGIN WORK;

CREATE OR REPLACE FUNCTION get_next_number()
    RETURNS character varying AS $$
DECLARE next_number VARCHAR;
BEGIN
    LOCK TABLE invoices IN SHARE MODE;

    SELECT format(
               'Invoice-%04s-%04s',
               EXTRACT(year FROM now()),
               to_char(COALESCE(regexp_replace(number, '[^0-9]','','g')::NUMERIC, 0) + 1, 'fm00000')
           ) INTO next_number
      FROM invoices
     WHERE EXTRACT(year FROM CURRENT_DATE) = EXTRACT(year FROM invoice_date)
     ORDER BY id DESC
     LIMIT 1;

    RETURN next_number;
END;
$$ LANGUAGE plpgsql;

INSERT INTO invoices
VALUES((SELECT get_next_number(), 
(...)));

COMMIT WORK;
0

Moze cos takiego bedzie delikanie lepsze

SELECT format(
            'Invoice-%04s-%04s',
            EXTRACT(year FROM CURRENT_DATE),
            to_char(COALESCE(regexp_replace(number, '[^0-9]','','g')::NUMERIC, 0) + 1, 'fm00000')
       )
 FROM invoices
WHERE invoice_date BETWEEN concat(EXTRACT(year FROM CURRENT_DATE), '-01-01')::date
                       AND concat(EXTRACT(year FROM CURRENT_DATE), '-12-31')::date
ORDER BY 1 DESC
LIMIT 1;
0

@poniatowski:

Pomedytuj nad tym, co naprawdę sensownie, choć delikatnie i minimalistycznie pisał wyżej @Mr.YaHooo

Dla mnie pisanie kodu o znaczeniu biznesowym (generowanie wysokopoziomowego numeru dokumentu) w SQL to "trochę" się spóźniłeś, o jakieś drobne 15 lat.

Kolejne, jak widzę "invoice" wbudowane w numer, to odzywają się we mnie wszystkie geny przodków z ziemi kieleckiej.
Next, padnie na próbie zrobienia fv korekcyjnej (praktycznie wszyscy chcą na to oddzielnej numeracji), czy zaliczkowej, proformy, które trzeba mieć w systemie w r 2022 itd...
Dorobienie Bardzo Ważnej Faktury 2go stycznia
Łamanie przez oddział / przedstawiciela
Nawet firma Janusz Szmatex Import Export 3ci garaż od lewej tego chce.

0

@ZrobieDobrze Bylo by latwiej jak bys napisac jak to zrobic. A juz najlepiej jak bys napisal krotki kod. Mozesz jakos dokladniej slowami opisac swoje podejscie?

Czyli generowanie numer w ogole bys nie opieral na bazie danych? lock na tabeli tez bys pominal? Czyli jak bys to zrobil?

Dla mnie ma sense w PHP wywolywac, kazda z ponizszych zapytan. Uwazam, jednak ze ciezko bedzie napisac sekwencyjny numer bez pobierania ostaniego z DB.

BEGIN;

LOCK table (...) ;

SELECT last_number (...) ;

INSERT INTO (...) ;

COMMIT;

Moge przeniesc logike biznesowac z SQL funkcji do PHP klasy. Chociaz moim zadniem oba podejscia sa latwe do zaktualizowania oraz do przetestowania. W peojekcje znajduje sie SQL migracja, wiej jakakolwiek zmiana to dosc prosta sprawa. Do tego moge napisac SQL testy. Trzymanie logiki w tym przypadku na poziomie DB to chyba tylo to, ze delikatnie szybciej sie wykona. Sam nie wiem... dwa podejscia wydaja sie ok.

1
poniatowski napisał(a):

@ZrobieDobrze Bylo by latwiej jak bys napisac jak to zrobic. A juz najlepiej jak bys napisal krotki kod.

  1. Wg mojej wizji nie uciekniesz od choćby częsciowego kodu w języku programowania (jaki ???), by nie mówić o wyższych warstwach, tam gdzie jest wysokopoziomowa wiedza jaki dok biznesowy jest aktualnie robiony. Dla mnie baza to trzymanie do kupy, reguły integralności, ale nie decyzje biznesowe. Podkreślam decyzje, ich implementacja - ale tylko impelmentacja - w SQL nie budzi mojego wielkiego sprzeciwu
  2. Adaptacja do typu dokumentu, której nie masz (level master: zmiana budowy numeru dokumentu na nowy rok)
  3. w tym konkretnym układ pomiedzy invoice_date a CURRENTDATE dla mnie intuicyjnie jest śliski
1
poniatowski napisał(a):

Moge przeniesc logike biznesowac z SQL funkcji do PHP klasy.

Zależy co chcesz w tym SQL
O ile trigger, w typowym układzie będzie zbyt słabo wyposażony w wiedzę, na jakiej powinien się oprzeć (typ, schemat numeracji właściwy na dany rok i kontekst biznesowy itd)
Procedura SQL z właściwym zestawem argumentów - lepiej. Ale ja osobiście procedur robiacych coś aktywnego nie lubię, choć przezyję (dwa miejsca kodu)

Powiedzenia "klasa PHP" ... hmmm ... to zależy jaka klasa, jak sie nazywa. Bo klas w ujęciu obiektowym bym widział wiecej, choćby tablica TypDokuemtu[] z której wybierasz jeden.

BTW właśnie testuję myślowo swoją własną hipotezę "co, jeśli z wysokopoziomowej warstwy nie dysponujemy wiarygodnym lockiem" ... katastrofy nie ma. Na potencjalnych konfliktach wychodzi mi najwyżej zmarnowanie numeru (baza OPRÓCZ pola stringowego z formatowanym numerem, ma 3 pola z grupową unikalnością: typ, nr mies (zero dla numeracji rocznej), numer (adekwatnie: w roku / w miesiacu))

0
ZrobieDobrze napisał(a):

BTW właśnie testuję myślowo swoją własną hipotezę "co, jeśli z wysokopoziomowej warstwy nie dysponujemy wiarygodnym lockiem" ... katastrofy nie ma. Na potencjalnych konfliktach wychodzi mi najwyżej zmarnowanie numeru (baza OPRÓCZ pola stringowego z formatowanym numerem, ma 3 pola z grupową unikalnością: typ, nr mies (zero dla numeracji rocznej), numer (adekwatnie: w roku / w miesiacu))

Co masz na myśli pisząc o zmarnowaniu numeru? Generalnie ustawa o rachunkowości mówi, że numeracja dokumentów musi być ciągła. To oznacza, że nie możesz pominąć żadnego numeru.

0
Fac napisał(a):
ZrobieDobrze napisał(a):

BTW właśnie testuję myślowo swoją własną hipotezę "co, jeśli z wysokopoziomowej warstwy nie dysponujemy wiarygodnym lockiem" ... katastrofy nie ma. Na potencjalnych konfliktach wychodzi mi najwyżej zmarnowanie numeru (baza OPRÓCZ pola stringowego z formatowanym numerem, ma 3 pola z grupową unikalnością: typ, nr mies (zero dla numeracji rocznej), numer (adekwatnie: w roku / w miesiacu))

Co masz na myśli pisząc o zmarnowaniu numeru? Generalnie ustawa o rachunkowości mówi, że numeracja dokumentów musi być ciągła. To oznacza, że nie możesz pominąć żadnego numeru.

A dokument który istniał, ale LUDZIE skasowali go ze względów wyższych ?

Tak, wiem, w duchu ustawy "anulowanie" by było właściwe. W realnym życiu i tak to wyłączają / porzucają procedurę anulowania. Dopóki w zakresie 1-2 dni, wiesz, ja nawet z tym nie walczę
Wniosek ? I tak frmy maja ręczne pomysły

BTW UoR mówi o numeracji dziennika
W moim scenariuszu wychodzi zmarnowanie tylko wtedy, gdy dwóch handlowców intensywnie klepie FV, i ten szybszy o milisekundy dostał wyjatek
(tu znów wraca pytanie: co odbija najważniejsze reguły walidacji czy podobne: kod SQL, czy kod warstwy, jak kod wyższy - do zmarnowania prawdopodobnie nie dojdzie)

2

A patrzyłeś na sekwencje? https://www.postgresql.org/docs/9.1/functions-sequence.html
Jak maiłby to robić to albo na sekwencjach i np. z nowym rokiem reset licznika.
Albo w odrębnej tabeli trzymać następny wolny numer a w tabeli z fakturami już sformatowany. I wtedy wraz z poprawnym insertem faktury dokonać aktualizacji tego licznika.
Pytanie co masz w warunkach biznesowych dokładnie. Czy są możliwe scenariusze kasowania faktury lub jakiegoś poprawienia numeracji?

0

Faktura, moze byc skasowana, tak.

1

Skoro faktura może być kasowana to ja poszedłbym w odrębną tabelę trzymająca aktualny licznik. Przy insert faktury odczyt obecnego licznika, stworzenie na jego podstawie dedykowanego formatu i insert wraz z pełnym numerem. To samo przy kasowaniu plus warunek, że kasować w pełni można tylko ostatnią fakturę.

0
jurek1980 napisał(a):

Skoro faktura może być kasowana to ja poszedłbym w odrębną tabelę trzymająca aktualny licznik. Przy insert faktury odczyt obecnego licznika, stworzenie na jego podstawie dedykowanego formatu i insert wraz z pełnym numerem. To samo przy kasowaniu plus warunek, że kasować w pełni można tylko ostatnią fakturę.

Jak się zgodzimy, że jest tabela wyliczająca wszystkie typy dokumentów i ich specyficzne zasady (w tym numeracji), to jest ważne. Ty nazywasz tabelą liczników (per year?), ja typów dokumentów (per year ??? nie mam pewnosci), podobne, przynajmniej na tym poziomie dochodzenia do dyskusji

Dla mnie prowadzenie licznika czy max(invoices) to już wtórne, kwesta implementacyjna.
Rzeczywiście zachowuje się odmiennie w przypadku kasowania ostatniej, zalezy jakie wymagania biznesu

1

@poniatowski: Czy ten pendig status wymaga już numeru? Czemu nie tworzycie więc jakiejś proformy z odrębnym numerem? Nie wiem dla jakiego kraju to robisz i czy tam nie ma jakichś innych zasad związanych z księgowością. Bo mam dziwne wrażenie, że wszyscy tu myślą w kontekście PL.

Edit na szybko znalazłem coś takiego https://www.codeguru.com/database/generate-complex-next-numbers-with-sql-server/

1

Nie znam się, ale się wypowiem ;)
Według mnie w bazie danych powinieneś mieć przynajmniej dwie kolumny dot. numeru. Jedną typu int - kolejny numer w miesiącu/roku oraz drugą typu string - pełny numer, który zawiera w sobie kolejny numer, miesiąc, rok ew. inne rzeczy typu jakiś symbol, serie etc.
Dodatkowo w tabeli powinieneś przechowywać schemat numeracji tj. czy to ma być NUMER/MM/RRRR czy NUMER/RRRR czy SYMBOL/NUMER/MM/RRRR itd.
Jak będziesz miał numer w postaci int to wydobycie kolejnego numeru jest banalne => musisz wybrać MAX Numer w danym miesiącu/roku w zakresie wybranej numeracji.

Dodatkowo uważam, że za nadanie kolejnego numeru w roku/miesiącu powinna odpowiadać baza danych a nie jakiś PHP (kto w ogole w tym jeszcze programuje?!).

Widzę również, że ponownie wróciła dyskusja dot. ciągłości numeracji więc zwrócę tylko uwagę, że to, że ustawa o VAT (a nie UoR) wymaga ciągłości w numeracji ale to nie znaczy, że program musi być mądrzejszy od użytkownika. Z zawodu jestem księgowym i wiem, że jest masa sytuacji, gdzie ten numer trzeba zmienić, poprawić, zastąpić, usunąć itd. Do czasu, gdy faktura nie trafi do obiegu (nie zostanie przekazana klientowi) można z nią zrobić praktycznie wszystko. A nawet jak trafi do obiegu to z praktycznego doświadczenia wiem, że firmy i tak często dokonują zmian na takich dokumentach z różnych powodów.

0
jurek1980 napisał(a):

A patrzyłeś na sekwencje? https://www.postgresql.org/docs/9.1/functions-sequence.html
Jak maiłby to robić to albo na sekwencjach i np. z nowym rokiem reset licznika.
Albo w odrębnej tabeli trzymać następny wolny numer a w tabeli z fakturami już sformatowany. I wtedy wraz z poprawnym insertem faktury dokonać aktualizacji tego licznika.
Pytanie co masz w warunkach biznesowych dokładnie. Czy są możliwe scenariusze kasowania faktury lub jakiegoś poprawienia numeracji?

Wydaje mi sie, ze dobrze podpowiadasz i pojde z sekwencja. Sama sekwecja to juz tabela z jednym wierszem. I nie musze nakladac LOCK na tabeli. Do tego usuniete faktury sa soft-delete wiec w sumie tez ok, bo na usunietych fakturach tez musze miec numer. Wiec w moim przypadku jest ok, nie wiem jak w innych.

Do tego chce przechowywac rok oraz caly, sformatowany numer w 2 oddzielnych kolumnach oraz sekwencja. Brzmi jak dobre rozwiazanie.

Teraz tylko zastanawiam sie jak restartowac ta sekwencje. Troche obawiam sie uzywania triggerow. Nie wiem czemu, ale zawsze mysle, ze ktos moze go usunac przypadkowo, bo nie bedzie rozumial poco on jest. Moge opcjonalnie z poziomu PHP sprawdzic czy last_invoice_year < current_year. Jezeli true to z PHP wywoloac zapytanie SQL i zrestartowac sekwencje. Nie wiem czy jest jakikolwiek sense umieszczania tej logiki w bazie danych.

1

Rozważ co zajmie Ci więcej czasu i będzie mniej kłopotliwe w utrzymaniu. Częste przypadki z praktyki

Klient zaczyna z system i stwierdza, że koszty będzie wprowadzał jednym rejestrem numerowanym w ramach roku. Więc luz tworzysz sekwencje i jedziesz w ramach roku od 1.
Gdzieś pod koniec roku, stwierdza, że numerowanie roczne jest bez sensu, bo faktury nie spływają na czas i często zdarza mu się że faktura z wyższym numerem jest starsza od wcześniejszych.
Postanawia, że od nowego roku zmienia numerację na miesięczną.
Teraz sytuacja się zmienia i kupuje coraz więcej z UE w eur. Więc postanawia dodać nowy symbol do wyróżnienia tych faktur.
I wspomniany tu już case z przełomem roku, idą już faktury z nowego, ale ciągle jest wprowadzany poprzedni.

Jeżeli uważasz, że sekwencja jest na tyle elastyczna, że to ogarnie to ok. IMO to się nie sprawdzi.

0
Panczo napisał(a):

Rozważ co zajmie Ci więcej czasu i będzie mniej kłopotliwe w utrzymaniu. Częste przypadki z praktyki

Klient zaczyna z system i stwierdza, że koszty będzie wprowadzał jednym rejestrem numerowanym w ramach roku. Więc luz tworzysz sekwencje i jedziesz w ramach roku od 1.
Gdzieś pod koniec roku, stwierdza, że numerowanie roczne jest bez sensu, bo faktury nie spływają na czas i często zdarza mu się że faktura z wyższym numerem jest starsza od wcześniejszych.
Postanawia, że od nowego roku zmienia numerację na miesięczną.
Teraz sytuacja się zmienia i kupuje coraz więcej z UE w eur. Więc postanawia dodać nowy symbol do wyróżnienia tych faktur.
I wspomniany tu już case z przełomem roku, idą już faktury z nowego, ale ciągle jest wprowadzany poprzedni.

Jeżeli uważasz, że sekwencja jest na tyle elastyczna, że to ogarnie to ok. IMO to się nie sprawdzi.

Troche nie rozumiem co polecasz? Moim zdaniem rozwiazanie z sekwencje jest wystarczajace. Jezeli format ulegnie zmianie to nie ma problemu. Dlatego w tabeli faktry trzymany jest caly juz sformatowany numer. Logike zawsze mozna zmienic, ktora jest odseparowana. Na razie na 100% nie potrzebuje miesiecy, to po cholere tracic czas i to implemoentowac oraz testowac. Cos co nie bedzie uzyte?

2

Troche nie rozumiem co polecasz?

Zrezygnować z sekwencji

Moim zdaniem rozwiazanie z sekwencje jest wystarczajace. Jezeli format ulegnie zmianie to nie ma problemu. Dlatego w tabeli faktry trzymany jest caly juz sformatowany numer. Logike zawsze mozna zmienic, ktora jest odseparowana. Na razie na 100% nie potrzebuje miesiecy, to po cholere tracic czas i to implemoentowac oraz testowac. Cos co nie bedzie uzyte?

To, że czegoś teraz nie potrzebujesz, nie znaczy, że tego nie będziesz potrzebował. Nawet jeżeli faktycznie tego nie będziesz potrzebował to skutecznie pomijasz argument o przełomie roku, którego raczej nie unikniesz. Więc jak przy pomocy sekwencji rozwiążesz wprowadzanie danych do 2 osobnych lat?

4

Sekwencje

  1. sekwencji powinieneś mieć tyle ile masz numerów startujących od jeden - resetowanie sekwencji to najgorsze co możesz zrobić. Jak masz faktury vat i korekty to MUSISZ mieć przynajmniej dwie sekwencje.
  2. jak się pani Krysi przypomni drugiego stycznie, że ma jeszcze fakturę, którą trzeba do grudnia dopisać (a już dodała 10 styczniowych) to jesteś w bardzo czarnej dupie - zresetowałeś sekwencję.
  3. jak podczas zapisu faktury cokolwiek się wywali i zrobisz rollback to NUMER JUŻ SIĘ ZWIĘKSZYŁ i masz dziurę w numeracji i jesteś w czarnej dupie.

Wszystko to można ogarnąć i się na to przygotować, tylko że nakład pracy będzie kosmiczny w porównaniu do dodatkowej tabeli z dwiema kolumnami - typ i numer, gdzie do pola typ wpiszesz cokolwiek co jednoznacznie identyfikuje numerator (np. rok, miesiąc, typ dokumentu i magazyn)

0
abrakadaber napisał(a):

Sekwencje

  1. sekwencji powinieneś mieć tyle ile masz numerów startujących od jeden - resetowanie sekwencji to najgorsze co możesz zrobić. Jak masz faktury vat i korekty to MUSISZ mieć przynajmniej dwie sekwencje.
  2. jak się pani Krysi przypomni drugiego stycznie, że ma jeszcze fakturę, którą trzeba do grudnia dopisać (a już dodała 10 styczniowych) to jesteś w bardzo czarnej dupie - zresetowałeś sekwencję.
  3. jak podczas zapisu faktury cokolwiek się wywali i zrobisz rollback to NUMER JUŻ SIĘ ZWIĘKSZYŁ i masz dziurę w numeracji i jesteś w czarnej dupie.

Wszystko to można ogarnąć i się na to przygotować, tylko że nakład pracy będzie kosmiczny w porównaniu do dodatkowej tabeli z dwiema kolumnami - typ i numer, gdzie do pola typ wpiszesz cokolwiek co jednoznacznie identyfikuje numerator (np. rok, miesiąc, typ dokumentu i magazyn)

To jest bardzo wartosciowa odpowiedz oraz argumenty przeciw sekwencji. 1 i 2 punkt w sumie w moim przypadku nie dotycza sie, ale bardzo, mega sluszne uwagi. Dziki Ci za nie. Za 3 punkt, rozwalil mnie, nie pomyslalem zupelnie o rollback'u. :facepalm. Super post. Powaznie, szacun @abrakadaber !

Jeszcze, zeby tylko podsumowac. Tabela, ktora sugerujesz ma wygladac jak ponizej?

Invoice_numbers
year
sequency

Wszystko dalej bedzie w SQL transaki oraz lock na invoice_numbers. Cos pominalem? Nie potrzebuje typu dokumentu i brak korekt. To sa pardziej takie paragony, troche dlugo to tlumaczyc, ale sam case jest znacznie prosty niz faktury.

W sumie to jak tak mysle, to chyba pierwszy pomysl byl jednak najlepiszy. Czyli w tej samej tabeli invoice trzymac kolumne rok , sekwencje oraz numer. Pozniej lock na tablie, pobieram max(sekwencja) na dany rok i od co. Duzo faktu nie bedzie, tylko kilka na caly miesiac.

edtit

Albo w ogole 2 kolumny, bez roku, bo jest invoice_date. Nastepnie w SQL mozna pobrac invoice_date BETWEEN concat(current_year, '-01-01')::DATE AND concat(current_year, '-12-31')::DATE. Jezeli cos zwroci to git jak nie ma nic to 1 i na koniec +1.

0

sugerowałem tabelkę z 2 polami - wyroznik (differentiator??) typu varchar oraz number (current_namber, last_number) jako int. Jak założysz indeks na oba pola (w kolejności wyróżnik, numer) to cały odczyt będzie leciał po indeksie.
Teraz dla Twojego AKTUALNEGO :p przypadku do pola wyroznik wstawiasz 4-znakowy ROK. Jak Ci dojdzie konieczność numeracji miesięcznej to NIC NIE ZMIENIASZ poza podawaniem nie samego roku ale rok (4 znaki) + miesiąc (2 znaki). Jak Ci się zmieni z rok/miesiąc na rok/typ to dalej NIC NIE ZMIENIASZ w logice tylko podajesz rok + typ i t odziała bez żadnych zmian.

Takie jest moje zdanie na ten temat. Co więcej takie coś działa u kilkudziesięciu klientów w PL od 25-30 lat i problemu nie ma. Niektórzy mają dzienny przyrost dokumentów idący w dziesiątki tysięcy (oczywiście z różnymi wyróżnikami).

0
abrakadaber napisał(a):

sugerowałem tabelkę z 2 polami - wyroznik (differentiator??) typu varchar oraz number (current_namber, last_number) jako int. Jak założysz indeks na oba pola (w kolejności wyróżnik, numer) to cały odczyt będzie leciał po indeksie.
Teraz dla Twojego AKTUALNEGO :p przypadku do pola wyroznik wstawiasz 4-znakowy ROK. Jak Ci dojdzie konieczność numeracji miesięcznej to NIC NIE ZMIENIASZ poza podawaniem nie samego roku ale rok (4 znaki) + miesiąc (2 znaki). Jak Ci się zmieni z rok/miesiąc na rok/typ to dalej NIC NIE ZMIENIASZ w logice tylko podajesz rok + typ i t odziała bez żadnych zmian.

Takie jest moje zdanie na ten temat. Co więcej takie coś działa u kilkudziesięciu klientów w PL od 25-30 lat i problemu nie ma. Niektórzy mają dzienny przyrost dokumentów idący w dziesiątki tysięcy (oczywiście z różnymi wyróżnikami).

Rozumiem, ze w invoice tabelce, bedzie number. Numer moze drastycznie sie zminic, zeby nie robic kombinacji w przyszlosci to najprosciej wydaje sie zapisac sformatowany numer.

Teraz musze zapisac, gdzies sekwencje (int) i moze rok, nie wiem czy zapis roku tak naprawde bedzie potrzebny. Jezeli cos sie zmieni to sekwencja kolumna moze byc usunieta albo zastopiona przy czym nie trace spojnosci z numerem faktury.

Latniej bylo by na przykladzie. Bo z tabelka sa juz dwie rozne wersje. 1 byla, zeby tylko trzymac jeden wiersz i podbijak sekwencje. Druga, zeby zapisywac wszystkie sekwencje. Czasem sa faktury bez sekwencji.

edit

create table invoice_number
(
    id          serial primary key,
    type        varchar(3) not null, -- invoice, receipt itd
    last_year   integer not null,
    last_number integer not null
);

create unique index invoice_number_unique_inx
    on payment_advance_repayments (last_year, last_number);

W tabeli jest zapisywany 1 rekord na caly typ faktury, paragonu itd.

W tym samamym momencie dwoch userow moze pobrac last_number, wiec trzeba dodac lock na tej tabelce. I wydaje sie, ze to wszystko?

0
poniatowski napisał(a):

Rozumiem, ze w invoice tabelce, bedzie number. Numer moze drastycznie sie zminic, zeby nie robic kombinacji w przyszlosci to najprosciej wydaje sie zapisac sformatowany numer.

Nie no, cały sformatowany numer tekstowy tak jak występuje na drukowanym dokumencie. Dodatkowo możesz trzymać sam numer do szybszego wyszukiwania

Teraz musze zapisac, gdzies sekwencje (int) i moze rok, nie wiem czy zapis roku tak naprawde bedzie potrzebny. Jezeli cos sie zmieni to sekwencja kolumna moze byc usunieta albo zastopiona przy czym nie trace spojnosci z numerem faktury.

Nie mam pojęcia o co tu chodzi

Latniej bylo by na przykladzie. Bo z tabelka sa juz dwie rozne wersje. 1 byla, zeby tylko trzymac jeden wiersz i podbijak sekwencje. Druga, zeby zapisywac wszystkie sekwencje. Czasem sa faktury bez sekwencji.

edit

create table invoice_number
(
    id          serial primary key,
    type        varchar(3) not null, -- invoice, receipt itd
    last_year   integer not null,
    last_number integer not null
);
  1. id w tym przypadku jest całkowicie zbędny - równie dobrze możesz założyć PK na trzech pozostałych polach
  2. pole nie jest last_year tylko year bo jego nie zmieniasz jak zacznie się nowy rok tylko dodajesz kolejny rekord z nowym rokiem i numerem = 0 (albo 1 w zależności jaki numer będziesz trzymał)
  3. nic nie stoi na przeszkodzie połączyć type i last_year w jednym polu a dostajesz większą elastyczność

create unique index invoice_number_unique_inx
on payment_advance_repayments (last_year, last_number);

nie możesz mieć unique na tych dwóch polach bo one nie identyfikują numeru - unique musisz mieć przede wszystkim na type year

W tabeli jest zapisywany 1 rekord na caly typ faktury, paragonu itd.

nie - zapisywany jest jeden rekord na typ i rok

W tym samamym momencie dwoch userow moze pobrac last_number, wiec trzeba dodac lock na tej tabelce. I wydaje sie, ze to wszystko?

nie na TABELĘ ale na rekord - najprościej przez SELECT 1 FROM tab WHERE rok = x AND typ = 'y' FOR UPDATE - lock jest automatycznie zdejmowany jak zrobisz rollback albo commit transakcji

0

Problem rozwiazany :) Poszedlem z nowa tabelka. Rozwiazanie proste w implementacji. Latwo zmienic format. Latwo dodwac nowe typy. Z lock na tabeli idzie ladnie zapanowac nad race condition. Potestowalem rozwiazanie, smiga bardzo dobrze, bez zarzutow. Dziki Pany!

0

@abrakadaber: Dzieki za sluszne uwagi.

create table invoice_number
(
    type        varchar(3) not null,
    year        integer not null,
    last_number integer not null
);

create unique index invoice_number_unique_inx
    on payment_advance_repayments (type, year);
2

Tak w ogóle jeśli zajmujesz się takimi rzeczami, to warto spotkać się z osobą, która obsługuje system do faktur w praktyce. Powie ci o wielu rzeczach, o których byś nawet nie pomyślał. Wydaje mi się, że próbujesz ponownie wynaleźć koło i na siłę sam kombinujesz nie znając specyfiki tematu.
Jeśli ma to być realny system, a nie jakaś wprawka programistyczna, to nie pomijaj etapu analizy - oszczędzisz sobie bólu głowy później (albo wk* klientów dzwoniących 31 grudnia, że czegoś nie mogą zrobić).

1 użytkowników online, w tym zalogowanych: 0, gości: 1