Posty bez odpowiedzi |oraz Aktywne tematy Dzisiaj jest 2024-11-25, 23:12x



Odpowiedz w temacie  [ Posty: 8 ] 
petelki 
Autor Wiadomość

Rejestracja: 2012-10-29, 20:13
Posty: 40
Post petelki
Mam 2 petle. Jedna jest zagniezdzona i gdy przejdzie ta zagniezdzona to wyskakuje blad. "Funkcja BOF lub EOF zwraca wartość prawdziwą lub bieżący rekord został usunięty" gdy ma isc od poczatku.

//inicjalizacja obiektu dokumentu wydania z magazynu w buforze

Kod:
long sub znajdz_magazyn(long idmg)
      
      long mgmagazyn

      BaseAll mg
      mg.setbase("MG")
      mg.setkey("id")
      mg.setkeyseg("id", idmg)

      znajdz_magazyn = mg.getfield("magazyn")
//   message using "%l", mgmagazyn

endsub


long sub rez_tow(long idmg)
   
dispatch con = GetAdoConnection()
dispatch rs = "ADODB.Recordset"
string querySQL, querySQL2, querySQL3, querySQL4
string qsql, qsql2
long ilosc2

long suma2 = 0
long suma
long x
long iloscre
string s


dispatch xDokMg
dispatch xTw, xMg, xKh
dispatch xRezerwacja
int err  //ZO 3630/12/2012 //PW 3266/12/2012
long mgm
long rez
string params
dispatch xDokumentMg
string towar2




message using "%l", idmg   
querySQL = "select ilosc as ilo, tw.kod as towarkod from hm.mg join hm.mz on mg.id =mz.super join hm.tw on mz.idtw=tw.id where mg.id="
qsql = using "%s'%l'",querySQL,idmg





if rs.State == 1 then rs.Close()
rs.Open(qsql, con, 3)

if rs.RecordCount > 0 then
  rs.MoveFirst()
  [u]while !rs.EOF[/u]

      
      iloscre = rs.Fields("ilo").Value*(-1)
      towar2 = rs.Fields("towarkod").Value


   
       

suma = iloscre
querySQL2 = "select  (case when SUM(rez.iloscrz) IS null then 0 else SUM(rez.iloscrz) end) as iloscc,zp.ilosc as zpilosc,zo.kod as zokod1,zo.id as zoid from hm.mg  join hm.mz on mg.id =mz.super  join hm.tw on mz.idtw = tw.id  join hm.zp on zp.idtw=tw.id  join hm.zo on zo.id=zp.super  left outer join (select rz.iloscrz,rz.idtw,zo.id from hm.zo  left outer join hm.RZ on zo.id=rz.iddokrz join hm.TW on tw.id=rz.idtw) as rez on rez.idtw=zp.idtw and rez.id=zo.id   where (zp.ilosc - (case when rez.iloscrz IS null then 0  else rez.iloscrz end)) > 0 and tw.kod="
querySQL3 = "group by zo.kod, zp.ilosc, zo.id having (zp.ilosc - (case when SUM(rez.iloscrz) IS null then 0 else SUM(rez.iloscrz) end)) > 0"


//case when rez.iloscrz IS null then 0 else rez.iloscrz end as iloscc
qsql2 = using "%s'%s' %s",querySQL2,towar2,querySQL3
if rs.State == 1 then rs.Close()
rs.Open(qsql2, con, 3)

if rs.RecordCount > 0 then
  rs.MoveFirst()
  [u]while  !rs.EOF[/u]
   
   message using "%s", rs.Fields("zokod1").Value
       s = rs.Fields("zokod1").Value
      s = using "numer='%s'", s
xDokMg = xFactory.NewObject("BDokZo")
message using "%s", s

err = xDokMg.Load(s) //ZO 3630/12/2012 //PW 3266/12/2012
if err then error "Błąd Load()"

xDokumentMg = xFactory.NewObject("BDokumentZo")
params = using "id=%l", xDokMg.item(1).id
err = xDokumentMg.Load(params)
if err then error "Błąd Load()"

xTw = xFactory.NewObject("BTw")
xMg = xFactory.NewObject("BMg")
xKh = xFactory.NewObject("BKh")

xRezerwacja = xFactory.NewObject("BRezerwacja")
err = xRezerwacja.Edit()
if err then error "Błąd Edit()"
xRezerwacja.typ = 2     // rezerwacja do pozycji
xRezerwacja.typrzrw = 1 // rezerwacja ilościowa
message using "%s", towar2
params = using "kod='%s'", towar2
message using "%s", params
xRezerwacja.towar = xTw.Give(params)
params = using "kod='%s'", xDokumentMg.danekh.khkod
xRezerwacja.kontrahent = xKh.Give(params)

mgm = znajdz_magazyn(idmg)
message using "%l", mgm

//string mgm =using "id=%l", znajdz_magazyn(zidmg)
params = using "id='%l'", mgm
xRezerwacja.magazyn = xMg.Give(params)

xRezerwacja.termin = xDokumentMg.dataSprzedazy
xRezerwacja.data = xDokumentMg.dataSprzedazy


suma = iloscre
x = rs.Fields("zpilosc").Value - rs.Fields("iloscc").Value
suma2 = suma2 + x


if suma2<suma then
   goto nastepny
      else
   
   x = suma-(suma2-x)
   

nastepny:
if rs.Fields("zpilosc").Value - rs.Fields("iloscc").Value > suma then
   
   x = suma
   
endif
message using "%l", x
xRezerwacja.ilosc = x //xDokumentMg.pozycje.item(1).ilosc 
message using "%l", x
// powiązanie rezerwacji z pozycją dokumentu magazynowego
xRezerwacja.iddokrz = xDokumentMg.id
xRezerwacja.bazadokrz = 33
xRezerwacja.idpozrz = xDokumentMg.pozycje.item(1).id
xRezerwacja.bazapozrz = 37

err = xRezerwacja.Save()
if err then error "Błąd Save()"
endif

if rs.Fields("zpilosc").Value - rs.Fields("iloscc").Value >= suma then
   exit
endif

if suma-(suma2-x)<=rs.Fields("zpilosc").Value then
   exit
endif



koniecpetli:

            rs.MoveNext()
      wend
endif   
      
rs.MoveNext()
   wend
endif   
            



endsub

/*
*/


2013-01-04, 21:42
Wyświetl profil
Autor Wiadomość
 


Ekspert
Ekspert

Rejestracja: 2007-11-16, 15:08
Posty: 4000
Pomógł: 448
Post Re: petelki
Chciałbym pomóc ale za dużo kodu do analizy jak dla mnie.

Napisz od początku kod który zawiera tylko te dwie pętle i zobacz jak po nich chodzi i co zwraca.

_________________
Skontaktuj się z Ekspertem | Zamów dodatek

tel. 22 7 538 538
ekspert@mojaSymfonia.pl
http://www.mojaSymfonia.pl


2013-01-04, 23:03
Wyświetl profil

Rejestracja: 2012-10-29, 20:13
Posty: 40
Post Re: petelki
Chyba wiem gdzie lezy problem. Po prostu jak otwieram pierwsze sql to na drugim jest zamykane rs. Jeszcze nie wiem jak do tego podejsc. 2 na raz nie da sie miec otwartych. ciekawe jak zrobic zeby sql pamietal te otwarte pierwsze zapytanie.


2013-01-05, 00:03
Wyświetl profil
Administrator

Rejestracja: 2007-11-15, 12:42
Posty: 102
Pomógł: 7
Post Re: petelki
Zadeklatuj rs i rs2 oddzielne dla kazdej petli

_________________
Administrator
forum.mojaSymfonia.pl


2013-01-05, 00:18
Wyświetl profil

Rejestracja: 2012-10-29, 20:13
Posty: 40
Post Re: petelki
dzieki. dziala. wszakze jeszcze musze troche posiedziec nad tym kodem, ale jedno z glowy:)


2013-01-05, 10:47
Wyświetl profil

Rejestracja: 2012-10-29, 20:13
Posty: 40
Post rezerwacje automatyczne
Mam tu taki kod, który za zadanie ma automatycznie rezerwować do zamówień z przyjęć towarów. Jak mam ZO na 2 identyczne towary to przy przyjęciu rezerwuje mi towar do pozycji jednej ZO i do pozycji 2 ZO. Tak jak by dublował rezerwacje, a ma wpisane w idpozrz konkretne id pozycji. Mimo tego tak jak napisałem rezerwuje się towar do 2 pozycji na ten sam towar.

Kod:
xRezerwacja.iddokrz = xDokumentMg.id
xRezerwacja.bazadokrz = 45

xRezerwacja.idpozrz = zpid //xDokumentMg.pozycje.item(1).id
xRezerwacja.bazapozrz = 30



Może coś z tym, ale nie wiem jak to zmodyfikować i czy w ogóle da się zamiast towaru podać konkretne id pozycji.

Kod:
params = using "kod='%s'", towar2

xRezerwacja.towar = xTw.Give(params)







Kod:
//inicjalizacja obiektu dokumentu wydania z magazynu w buforze

long sub znajdz_magazyn(long idmg)
      
      long mgmagazyn

      BaseAll mg
      mg.setbase("MG")
      mg.setkey("id")
      mg.setkeyseg("id", idmg)

      znajdz_magazyn = mg.getfield("magazyn")
//   message using "%l", mgmagazyn

endsub


long sub rez_tow(long idmg)
   
dispatch con = GetAdoConnection()
dispatch rs = "ADODB.Recordset"
dispatch rs2 = "ADODB.Recordset"
string querySQL, querySQL2, querySQL3, querySQL4
string qsql, qsql2
float ilosc2

float suma2 = 0.0
float suma
float x
float iloscre
string s
float w


dispatch xDokMg
dispatch xTw, xMg, xKh
dispatch xRezerwacja
int err  //ZO 3630/12/2012 //PW 3266/12/2012
long mgm
long rez
string params
dispatch xDokumentMg
string towar2
long zpid
long zoid


   
querySQL = "select ilosc as ilo, tw.kod as towarkod from hm.mg join hm.mz on mg.id =mz.super join hm.tw on mz.idtw=tw.id where mg.id="
qsql = using "%s'%l'",querySQL,idmg





if rs.State == 1 then rs.Close()

rs.Open(qsql, con, 3)


  rs.MoveFirst()
  while !rs.EOF
//   if rs.eof then
   //message "nie ma nic"
//else
      
      iloscre = rs.Fields("ilo").Value*(-1)
      towar2 = rs.Fields("towarkod").Value
      


   
       

suma = iloscre
//querySQL2 = "select zp.id as zpid, (case when SUM(rz.iloscrz) IS null then 0 else SUM(rz.iloscrz) end) as iloscc,zp.ilosc as zpilosc,zo.kod as zokod1,zo.id as zoid ,zp.lp from hm.ZO join hm.ZP on zp.super=zo.id left outer join hm.RZ on rz.iddokrz=zo.id where zo.data>='2013-01-21' and zp.kod="
//querySQL3 = "group by zp.id, zo.kod,zp.lp, zo.id,zp.ilosc having (zp.ilosc - (case when SUM(rz.iloscrz) IS null then 0 else SUM(rz.iloscrz) end)) > 0 order by zoid"
querySQL2 = "select zp.id as zpid,(case when SUM(rez.iloscrz) IS null then 0 else SUM(rez.iloscrz) end) as iloscc, zp.ilosc as zpilosc,zo.kod as zokod1,zo.id as zoid ,zp.lp from hm.ZO join hm.ZP on zp.super=zo.id join hm.TW on zp.idtw=tw.id left outer join (select rz.iloscrz,rz.idtw,zo.id from hm.zo  left outer join hm.RZ on zo.id=rz.iddokrz join hm.TW on tw.id=rz.idtw)  as rez on rez.idtw=zp.idtw and rez.id=zo.id   where zo.data>='2013-01-21' and tw.kod="
querySQL3 = "group by zp.id, zo.kod,zp.lp, zo.id,zp.ilosc  having (zp.ilosc - (case when SUM(rez.iloscrz) IS null then 0 else SUM(rez.iloscrz) end)) > 0 order by zoid"
//querySQL2 = "select zo.kod as zokod1,zp.id as zpid,zp.lp,(case when SUM(rz.iloscrz) IS null then 0 else SUM(rz.iloscrz) end) as iloscc,zo.id as zoid,zp.ilosc as zpilosc from hm.ZO join hm.zp on zp.super=zo.id left outer join hm.RZ on rz.iddokrz=zo.id  where zp.kod="
//querySQL3 = "group by zo.kod,zp.lp,zp.id,zo.id,zp.ilosc having (zp.ilosc - (case when SUM(rz.iloscrz) IS null then 0 else SUM(rz.iloscrz) end)) > 0 order by zo.id"
//case when rez.iloscrz IS null then 0 else rez.iloscrz end as iloscc
qsql2 = using "%s'%s' %s",querySQL2,towar2,querySQL3
if rs2.State == 1 then rs2.Close()
rs2.Open (qsql2, con, 3)

if rs2.eof then
   message "pusty"
      goto koniecpetli2

endif
  rs2.MoveFirst()
  while  !rs2.EOF
   
   
      zpid = rs2.Fields("zpid").Value
      zoid =rs2.Fields("zoid").Value
       s = rs2.Fields("zokod1").Value
      s = using "numer='%s'", s
xDokMg = xFactory.NewObject("BDokZo")


err = xDokMg.Load(s) //ZO 3630/12/2012 //PW 3266/12/2012
if err then error "Błąd Load()"

xDokumentMg = xFactory.NewObject("BDokumentZo")
params = using "id=%l", zoid
err = xDokumentMg.Load(params)

if err then error "Błąd Load()"

xTw = xFactory.NewObject("BTw")
xMg = xFactory.NewObject("BMg")
xKh = xFactory.NewObject("BKh")

xRezerwacja = xFactory.NewObject("BRezerwacja")
err = xRezerwacja.Edit()
if err then error "Błąd Edit()"
xRezerwacja.typ = 2    // rezerwacja do pozycji
xRezerwacja.typrzrw = 2 //1 // rezerwacja ilościowa

params = using "kod='%s'", towar2

xRezerwacja.towar = xTw.Give(params)
params = using "kod='%s'", xDokumentMg.danekh.khkod
xRezerwacja.kontrahent = xKh.Give(params)

mgm = znajdz_magazyn(idmg)


//string mgm =using "id=%l", znajdz_magazyn(zidmg)
params = using "id='%l'", mgm
xRezerwacja.magazyn = xMg.Give(params)

xRezerwacja.termin = xDokumentMg.dataSprzedazy
xRezerwacja.data = xDokumentMg.dataSprzedazy


suma = iloscre

x = rs2.Fields("zpilosc").Value - rs2.Fields("iloscc").Value

suma2 = suma2 + x



if suma2<suma then
   goto nastepny
      else
   
   x = suma-(suma2-x)
   message using "%d", x
   w = x
   goto nastepny2
   

nastepny:
if rs2.Fields("zpilosc").Value - rs2.Fields("iloscc").Value > suma then
   
   x = suma
   
endif

nastepny2:
if x <= 0 then
   exit
endif

xRezerwacja.ilosc = x //xDokumentMg.pozycje.item(1).ilosc 

// powiązanie rezerwacji z pozycją dokumentu magazynowego
xRezerwacja.iddokrz = xDokumentMg.id
xRezerwacja.bazadokrz = 45

xRezerwacja.idpozrz = zpid //xDokumentMg.pozycje.item(1).id
xRezerwacja.bazapozrz = 30

err = xRezerwacja.Save()
if err then error "Błąd Save()"
message using "Zarezerwowano towar %s w ilości %f do zamówienia %s", towar2,x,s
endif



//if rs2.Fields("zpilosc").Value - rs2.Fields("iloscc").Value >= suma then
   //exit
//endif





if x== w  then
   exit
endif




koniecpetli:

            rs2.MoveNext()
      wend

koniecpetli2:

suma2 = 0.0   
   message "przechodzi"
//if x ==suma-(suma2-x) then
   //exit
//endif   
rs.MoveNext()
//endif
   wend
   
            



endsub

/*
*/


2013-01-22, 13:56
Wyświetl profil
Ekspert
Ekspert

Rejestracja: 2007-11-16, 15:08
Posty: 4000
Pomógł: 448
Post Re: petelki
U nie wywala się na
Kod:
xRezerwacja.ilosc = 1


Wersja 2012a


Załączniki:
hmfrez01.jpg
hmfrez01.jpg [ 9.32 KiB | Przeglądany 3603 razy ]

_________________
Skontaktuj się z Ekspertem | Zamów dodatek

tel. 22 7 538 538
ekspert@mojaSymfonia.pl
http://www.mojaSymfonia.pl
2013-01-22, 16:01
Wyświetl profil

Rejestracja: 2012-10-29, 20:13
Posty: 40
Post Re: Rezerwacje auto
1. Wywołujemy metodę "rez_tow" w procedurze dokumentu magazynowego. OnAfterInsert

Kod:
#include "rezerwacja"
OnAfterInsert = rez_tow(lId)


raport rezerwacja:

Kod:
//inicjalizacja obiektu dokumentu wydania z magazynu w buforze

long sub znajdz_magazyn(long idmg)
     
      long mgmagazyn

      BaseAll mg
      mg.setbase("MG")
      mg.setkey("id")
      mg.setkeyseg("id", idmg)

      znajdz_magazyn = mg.getfield("magazyn")
//   message using "%l", mgmagazyn

endsub


long sub rez_tow(long idmg)
   
dispatch con = GetAdoConnection()
dispatch rs = "ADODB.Recordset"
dispatch rs2 = "ADODB.Recordset"
string querySQL, querySQL2, querySQL3, querySQL4
string qsql, qsql2
float ilosc2

float suma2 = 0.0
float suma
float x
float iloscre
string s
float w


dispatch xDokMg
dispatch xTw, xMg, xKh
dispatch xRezerwacja
int err  //ZO 3630/12/2012 //PW 3266/12/2012
long mgm
long rez
string params
dispatch xDokumentMg
string towar2
long zpid
long zoid


   
querySQL = "select ilosc as ilo, tw.kod as towarkod from hm.mg join hm.mz on mg.id =mz.super join hm.tw on mz.idtw=tw.id where mg.id="
qsql = using "%s'%l'",querySQL,idmg





if rs.State == 1 then rs.Close()

rs.Open(qsql, con, 3)


  rs.MoveFirst()
  while !rs.EOF
//   if rs.eof then
   //message "nie ma nic"
//else
     
      iloscre = rs.Fields("ilo").Value*(-1)
      towar2 = rs.Fields("towarkod").Value
     


   
       

suma = iloscre
//querySQL2 = "select zp.id as zpid, (case when SUM(rz.iloscrz) IS null then 0 else SUM(rz.iloscrz) end) as iloscc,zp.ilosc as zpilosc,zo.kod as zokod1,zo.id as zoid ,zp.lp from hm.ZO join hm.ZP on zp.super=zo.id left outer join hm.RZ on rz.iddokrz=zo.id where zo.data>='2013-01-21' and zp.kod="
//querySQL3 = "group by zp.id, zo.kod,zp.lp, zo.id,zp.ilosc having (zp.ilosc - (case when SUM(rz.iloscrz) IS null then 0 else SUM(rz.iloscrz) end)) > 0 order by zoid"
querySQL2 = "select zp.id as zpid,(case when SUM(rez.iloscrz) IS null then 0 else SUM(rez.iloscrz) end) as iloscc, zp.ilosc as zpilosc,zo.kod as zokod1,zo.id as zoid ,zp.lp from hm.ZO join hm.ZP on zp.super=zo.id join hm.TW on zp.idtw=tw.id left outer join (select rz.iloscrz,rz.idtw,zo.id from hm.zo  left outer join hm.RZ on zo.id=rz.iddokrz join hm.TW on tw.id=rz.idtw)  as rez on rez.idtw=zp.idtw and rez.id=zo.id   where zo.data>='2013-01-21' and tw.kod="
querySQL3 = "group by zp.id, zo.kod,zp.lp, zo.id,zp.ilosc  having (zp.ilosc - (case when SUM(rez.iloscrz) IS null then 0 else SUM(rez.iloscrz) end)) > 0 order by zoid"
//querySQL2 = "select zo.kod as zokod1,zp.id as zpid,zp.lp,(case when SUM(rz.iloscrz) IS null then 0 else SUM(rz.iloscrz) end) as iloscc,zo.id as zoid,zp.ilosc as zpilosc from hm.ZO join hm.zp on zp.super=zo.id left outer join hm.RZ on rz.iddokrz=zo.id  where zp.kod="
//querySQL3 = "group by zo.kod,zp.lp,zp.id,zo.id,zp.ilosc having (zp.ilosc - (case when SUM(rz.iloscrz) IS null then 0 else SUM(rz.iloscrz) end)) > 0 order by zo.id"
//case when rez.iloscrz IS null then 0 else rez.iloscrz end as iloscc
qsql2 = using "%s'%s' %s",querySQL2,towar2,querySQL3
if rs2.State == 1 then rs2.Close()
rs2.Open (qsql2, con, 3)

if rs2.eof then
   message "pusty"
      goto koniecpetli2

endif
  rs2.MoveFirst()
  while  !rs2.EOF
   
   
      zpid = rs2.Fields("zpid").Value
      zoid =rs2.Fields("zoid").Value
       s = rs2.Fields("zokod1").Value
      s = using "numer='%s'", s
xDokMg = xFactory.NewObject("BDokZo")


err = xDokMg.Load(s) //ZO 3630/12/2012 //PW 3266/12/2012
if err then error "Błąd Load()"

xDokumentMg = xFactory.NewObject("BDokumentZo")
params = using "id=%l", zoid
err = xDokumentMg.Load(params)

if err then error "Błąd Load()"

xTw = xFactory.NewObject("BTw")
xMg = xFactory.NewObject("BMg")
xKh = xFactory.NewObject("BKh")

xRezerwacja = xFactory.NewObject("BRezerwacja")
err = xRezerwacja.Edit()
if err then error "Błąd Edit()"
xRezerwacja.typ = 2    // rezerwacja do pozycji
xRezerwacja.typrzrw = 2 //1 // rezerwacja ilościowa

params = using "kod='%s'", towar2

xRezerwacja.towar = xTw.Give(params)
params = using "kod='%s'", xDokumentMg.danekh.khkod
xRezerwacja.kontrahent = xKh.Give(params)

mgm = znajdz_magazyn(idmg)


//string mgm =using "id=%l", znajdz_magazyn(zidmg)
params = using "id='%l'", mgm
xRezerwacja.magazyn = xMg.Give(params)

xRezerwacja.termin = xDokumentMg.dataSprzedazy
xRezerwacja.data = xDokumentMg.dataSprzedazy


suma = iloscre

x = rs2.Fields("zpilosc").Value - rs2.Fields("iloscc").Value

suma2 = suma2 + x



if suma2<suma then
   goto nastepny
      else
   
   x = suma-(suma2-x)
   message using "%d", x
   w = x
   goto nastepny2
   

nastepny:
if rs2.Fields("zpilosc").Value - rs2.Fields("iloscc").Value > suma then
   
   x = suma
   
endif

nastepny2:
if x <= 0 then
   exit
endif

xRezerwacja.ilosc = x //xDokumentMg.pozycje.item(1).ilosc 

// powiązanie rezerwacji z pozycją dokumentu magazynowego
xRezerwacja.iddokrz = xDokumentMg.id
xRezerwacja.bazadokrz = 45

xRezerwacja.idpozrz = zpid //xDokumentMg.pozycje.item(1).id
xRezerwacja.bazapozrz = 30

err = xRezerwacja.Save()
if err then error "Błąd Save()"
message using "Zarezerwowano towar %s w ilości %f do zamówienia %s", towar2,x,s
endif



//if rs2.Fields("zpilosc").Value - rs2.Fields("iloscc").Value >= suma then
   //exit
//endif





if x== w  then
   exit
endif




koniecpetli:

            rs2.MoveNext()
      wend

koniecpetli2:

suma2 = 0.0   
   message "przechodzi"
//if x ==suma-(suma2-x) then
   //exit
//endif   
rs.MoveNext()
//endif
   wend
   
           



endsub



2. Tworzymy ZO
3. Tworzymy PW na ten towar z ZO

Gdy tworzymy 2 towary na ZO takie same to tworzy rezerwacje do tych 2 pozycji.


2013-01-23, 10:59
Wyświetl profil
Wyświetl posty nie starsze niż:  Sortuj wg  
Odpowiedz w temacie   [ Posty: 8 ] 

Kto jest online

Użytkownicy przeglądający to forum: Nie ma żadnego zarejestrowanego użytkownika i 19 gości


Nie możesz tworzyć nowych tematów
Nie możesz odpowiadać w tematach
Nie możesz zmieniać swoich postów
Nie możesz usuwać swoich postów
Nie możesz dodawać załączników

Szukaj:
Przejdź do:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Support forum phpbb by phpBB Assistant