diff --git a/app/Config/Email.php b/app/Config/Email.php index 409f71f..4b4a64d 100644 --- a/app/Config/Email.php +++ b/app/Config/Email.php @@ -30,7 +30,12 @@ class Email extends BaseConfig /** * SMTP Server Hostname */ - public string $SMTPHost ="web110.dogado.de"; + public string $SMTPHost = 'web110.dogado.de'; + + /** + * Which SMTP authentication method to use: login, plain + */ + public string $SMTPAuthMethod = 'login'; /** * SMTP Username diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 7f86c73..987c85a 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -4,30 +4,37 @@ use CodeIgniter\Router\RouteCollection; /** @var RouteCollection $routes */ - - - // We get a performance increase by specifying the default // route since we don't have to scan directories. $routes->get('/', 'Home::overview'); -$routes->get('Ajax/(:segment)', 'Ajax::$1'); -$routes->post('Ajax/(:segment)', 'Ajax::$1'); -$routes->get('newBill', 'Home::newBill'); -$routes->get('newTransfer', 'Home::newTransfer'); -$routes->get('newScheduled', 'Home::newScheduled/0'); -$routes->get('newScheduledTransfer', 'Home::newScheduled/1'); + +//ajax requests for Tableview +$routes->get('viewInvoice/(:any)', 'FormHandler::viewInvoice/$1'); +$routes->post('edit', 'FormHandler::editForm'); + +$routes->get('newScheduled', 'Scheduled::new/0'); +$routes->get('newScheduledTransfer', 'Scheduled::new/1'); +$routes->get('editScheduled/(:num)', 'Scheduled::edit/$1'); +$routes->get('getScheduledData', 'Scheduled::getTabData'); +$routes->post('deleteScheduled', 'Scheduled::delete'); +$routes->get('syncScheduled', 'Scheduled::sync'); +$routes->get('viewScheduled', 'Scheduled::viewScheduled'); -$routes->post('newBill', 'Home::attemptNewBilling'); -$routes->post('editAccount', 'Home::attemptEditAccount'); -$routes->get('editBill/(:num)', 'Home::editBill/$1'); -$routes->get('editScheduled/(:num)', 'Home::editScheduled/$1'); -$routes->get('syncScheduled', 'Home::syncScheduled'); +$routes->get('getOverviewData', 'Bills::getOverviewData'); +$routes->get('getAccountsBalance', 'Bills::getAccountsBalance'); +$routes->get('getToDosData', 'Bills::getToDosData'); +$routes->post('getActivitiesData', 'Bills::getActivitiesData'); +$routes->get('newBill', 'Bills::edit/0'); +$routes->get('newTransfer', 'Bills::edit/0/1'); +$routes->get('editBill/(:num)', 'Bills::edit/$1'); +$routes->get('viewActivities', 'Bills::viewActivities'); +$routes->post('deleteBill', 'Bills::delete'); + +$routes->post('editAccount', 'Accounts::attemptEditAccount'); +$routes->match(['GET', 'POST'],'viewAccounts', 'Accounts::index'); + -$routes->get('viewAccounts', 'Home::viewAccountsTree'); -$routes->get('viewActivities', 'Home::viewAccountActivities'); -$routes->get('viewScheduled', 'Home::viewScheduled'); -$routes->get('viewBill/(:any)', 'Home::viewBill/$1'); $routes->get('viewChart', 'Charts::getData/0'); $routes->get('viewChart/(:num)', 'Charts::getData/$1'); diff --git a/app/Controllers/Accounts.php b/app/Controllers/Accounts.php new file mode 100644 index 0000000..e52d8e7 --- /dev/null +++ b/app/Controllers/Accounts.php @@ -0,0 +1,44 @@ +accounts = new mAccounts; + } + + public function index(){ + if ($this->request->is('post')) { + return $this->editAccount(); + } + else + return $this->viewAccountsTree(); + } + + private function viewAccountsTree($data = array()){ + helper('form'); + $data = array_merge($data,$this->accounts->getTreeData()); + $data['ddlist'] = $this->accounts->getDropDownList( 'all', false,true ); + return view('accountstree',$data); + } + + private function editAccount(){ + $rules = [ + 'description' => 'required', + ]; + if (! $this->validate($rules)) { + return $this->viewAccountsTree(['errors'=>$this->validator->getErrors()]); + } + $saveDB = array(); + if ($this->accounts->saveAccount($this->request->getPost())) + $saveDB = ['mode'=>'alert-success','message'=>'Erfolgreich gespeichert']; + else + $saveDB = ['mode'=>'alert-danger','message'=>'Speichern fehlgeschlagen']; + + return $this->viewAccountsTree(['saveDB'=>$saveDB]); + } +}; \ No newline at end of file diff --git a/app/Controllers/Ajax.php b/app/Controllers/Ajax.php deleted file mode 100644 index 338bcef..0000000 --- a/app/Controllers/Ajax.php +++ /dev/null @@ -1,71 +0,0 @@ -session = service('session'); - } - - public function getTabelData(){ - if (!$this->request->isAJAX()) return; - $billing = model('App\Models\mBills'); - $start = $this->request->getPost('datum_start')??session('datum_start'); - $ende = $this->request->getPost('datum_ende')??session('datum_ende'); - $src = intval($this->request->getPost('source')) ?? 0; - $_SESSION['datum_start'] = $start; - $_SESSION['datum_ende'] = $ende; - $_SESSION['source'] = $src; - $result = $billing->getEntriesDet($start,$ende,$src); - return json_encode($result); - } - - public function getAccountsBalance(){ - if (!$this->request->isAJAX()) return; - $billing = model('App\Models\mBills'); - return json_encode($billing->getAccountsBalance()); - } - - public function getScheduledData(){ - if (!$this->request->isAJAX()) return; - $scheduled = model('App\Models\mScheduled'); - return json_encode($scheduled->getEntries()); - } - - public function getToDosData(){ - if (!$this->request->isAJAX()) return; - $billing = model('App\Models\mBills'); - return json_encode($billing->getToDos()); - } - - public function getOverviewData(){ - if (!$this->request->isAJAX()) return; - $billing = model('App\Models\mBills'); - $result = $billing->getEntries(); - return json_encode($result); - } - - public function deleteBill(){ - if (!$this->request->isAJAX()) return; - $bills = model('App\Models\mBills'); - $bills->deleteEntry($this->request->getPost('id')); - return json_encode(["result"=>"done"]); - } - - public function deleteScheduled(){ - if (!$this->request->isAJAX()) return; - $scheduled = model('App\Models\mScheduled'); - $scheduled->delete($this->request->getPost('id')); - return json_encode(["result"=>"done"]); - } -} - -?> \ No newline at end of file diff --git a/app/Controllers/Bills.php b/app/Controllers/Bills.php new file mode 100644 index 0000000..15f1e4c --- /dev/null +++ b/app/Controllers/Bills.php @@ -0,0 +1,90 @@ +transfer = $transfer; + $eBooking->scheduled = false; + $data = $eBooking->getNew(); + } else { + $billModel = model(mBills::class); + $bill = $billModel->find($id); /* @var \App\Entity\eBooking $bill */ + $bill->datum = date( "Y-m-d", strtotime( $bill->datum )); + $data = $bill->fillDetails(); + log_message('debug','Bills::edit:ID:'.$id.'; Data: '.json_encode($bill->toArray())); + } + return $this->viewBill($data); + } + + public function viewActivities(){ + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $data = array(); + $data['source'] = 0; + $data['start'] = session('datum_start') ?? date("Y-m-d", strtotime( "- 2 weeks", strtotime( "now" ) )); + $data['ende'] = session('datum_ende') ?? date("Y-m-d", strtotime( "now" ) ); + $data['source'] = session('source') ?? 0; + $data['sourcelist'] = $accounts->getDropDownList( 'all', true ); + return view('activities', $data); + } + + public function getOverviewData(){ + if (!$this->request->isAJAX()) return; + $billing = model('App\Models\mBills'); + $result = $billing->getEntries(10); + return json_encode($result); + } + + public function getActivitiesData(){ + if (!$this->request->isAJAX()) return; + $billing = model('App\Models\mBills'); + $start = $this->request->getPost('datum_start')??session('datum_start'); + $ende = $this->request->getPost('datum_ende')??session('datum_ende'); + $src = intval($this->request->getPost('source')); + $_SESSION['datum_start'] = $start; + $_SESSION['datum_ende'] = $ende; + $_SESSION['source'] = $src; + $result = $billing->getEntriesDet($start,$ende,$src); + return json_encode($result); + } + + public function delete(){ + if ($this->request->is('post')) { + if (!ctype_digit($this->request->getPost("id"))) return $this->failForbidden("Ungültige ID"); + log_message('debug',"delete id: ".$this->request->getPost("id")); + $billModel = model(mBills::class); + $result = $billModel->deleteEntry(intval($this->request->getPost("id"))); + if ($result !== true) + return $this->fail("Database error ".$result); + else + return $this->respondDeleted(['url'=>session('redirect_url') ?? site_url('/'),'ID'=>$this->request->getPost("id")]); + } + return $this->failUnauthorized("Schäm Dich"); + } + + public function getToDosData(){ + if (!$this->request->isAJAX()) return; + $billing = model('App\Models\mBills'); + return json_encode($billing->getToDos()); + } + + public function getAccountsBalance(){ + if (!$this->request->isAJAX()) return; + $billing = model('App\Models\mBills'); + return json_encode($billing->getAccountsBalance()); + } +} diff --git a/app/Controllers/Charts.php b/app/Controllers/Charts.php index 8d4acee..7fbf598 100644 --- a/app/Controllers/Charts.php +++ b/app/Controllers/Charts.php @@ -10,26 +10,32 @@ class Charts extends BaseController { $this->db = \Config\Database::connect(); } - public function getData($id){ + public function getData(int $id){ if ($id == 0) return $this->getAllData(); else return $this->getDetailData($id); } - private function getDetailData($id){ + private function getDetailData(int $id){ + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $bills = model('App\Models\mBills'); /* @var \App\Models\mBills $bills */ + $billDet = model('App\Models\mBillDetails'); /* @var \App\Models\mBills $billDet */ + $accTable = $accounts->getTable(); + $billTable = $bills->getTable(); + $billDetTable = $billDet->getTable(); $jetzt = date('Y-m-01'); $query = <<= '$jetzt' AND (datum < '$jetzt'::date + interval '1 month') - AND budget_bills.id = bill_id - AND account_id = finanzen.budget_accounts.id + AND $billTable.id = bill_id + AND account_id = $accTable.id AND (account_id IN ( WITH RECURSIVE subordinates AS ( SELECT @@ -37,7 +43,7 @@ class Charts extends BaseController { parent_id, description FROM - finanzen.budget_accounts + $accTable WHERE id = $id UNION @@ -46,7 +52,7 @@ class Charts extends BaseController { e.parent_id, e.description FROM - finanzen.budget_accounts e + $accTable e INNER JOIN subordinates s ON s.id = e.parent_id) SELECT id @@ -72,16 +78,24 @@ class Charts extends BaseController { return view("charts/monthly", $data); } private function getAllData(){ + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $bills = model('App\Models\mBills'); /* @var \App\Models\mBills $bills */ + $billDet = model('App\Models\mBillDetails'); /* @var \App\Models\mBills $billDet */ + $accTable = $accounts->getTable(); + $billTable = $bills->getTable(); + $billDetTable = $billDet->getTable(); $jetzt = date('Y-m-01'); $query = <<= '$jetzt' - AND budget_bills.id = bill_id - AND (account_id IN + AND $billTable.deleted_at is null + AND $billDetTable.deleted_at is null + AND $billTable.id = $billDetTable.bill_id + AND (account_id IN ( WITH RECURSIVE subordinates AS ( SELECT @@ -89,7 +103,7 @@ class Charts extends BaseController { parent_id, description FROM - finanzen.budget_accounts + $accTable WHERE id = w.id UNION @@ -98,7 +112,7 @@ class Charts extends BaseController { e.parent_id, e.description FROM - finanzen.budget_accounts e + $accTable e INNER JOIN subordinates s ON s.id = e.parent_id) SELECT id @@ -109,6 +123,7 @@ class Charts extends BaseController { EOD; $data = array(); $result = $this->db->query($query); + log_message('debug','Charts::getAllData: '.$this->db->getLastQuery()); foreach ($result->getResult() as $row) { $werte[] = $row->summe; $ids[] = $row->id; diff --git a/app/Controllers/FormHandler.php b/app/Controllers/FormHandler.php new file mode 100644 index 0000000..a794f03 --- /dev/null +++ b/app/Controllers/FormHandler.php @@ -0,0 +1,195 @@ +request->is('post')) { + log_message('debug','Bills::edit: post data: '.json_encode($this->request->getPost())); + return $this->checkBillForm(); + } + else + return $this->viewBill($data); + } + + /** + * Prüft ob Kategorie vorhanden wenn ein Betrag eingegeben wurde, ohne Betrag und ohne Kategorie wird die Zeile ignoriert bzw. gelöscht + */ + public function _ruleBookingLineAcc($value, array $data, ?string &$error, string $field){ + log_message('debug',"Bills::_ruleBookingLineAcc Typ von übergegeben Wert".gettype($value)); + $index = explode('.', $field)[1]; + if ($value > 0) return true; + + if ($index == 0) + $error = "Kategorie fehlt"; + else if ((is_numeric($data['input'][$index]) && (round($data['input'][$index],2) > 0.0)) || (is_numeric($data['output'][$index]) && (round($data['output'][$index]) > 0.0))) + $error = "Kategorie in Zeile " . $index . " fehlt"; + + return empty($error); + } + + /** + * Prüft ob Teilbetrag vorhanden wenn eine Kategorie ausgewählt wurde, ohne Betrag und ohne Kategorie wird die Zeile ignoriert bzw. gelöscht + * auch wird geprüft dass nur Input oder Output einen Wert enthält, soll im Javascript gemacht werden wenn ordentlich läuft + */ + public function _ruleBookingLineAmount(string $value, array $data, ?string &$error, string $field){ + log_message('debug',"Bills::_ruleBookingLineAmount Typ von übergegeben Wert ".gettype($value)); + $index = explode('.', $field)[1]; + if ((is_numeric($data['input'][$index]) && (round($data['input'][$index],2) > 0.0)) xor + (is_numeric($data['output'][$index]) && (round($data['output'][$index],2) > 0.0))) + return true; + + if ((is_numeric($data['input'][$index]) && (round($data['input'][$index],2) > 0.0)) && (is_numeric($data['output'][$index]) && (round($data['output'][$index],2) > 0.0))) + $error = "Systemfehler: Nur Input oder Output darf einen Wert haben"; + + else if (($index == 0) && (count($data['input'])==1)) + $error = "Betrag fehlt"; + else if (is_numeric($data['account_id'][$index]) && ($data['account_id'][$index] > 0)) + $error = "Betrag in Zeile " . $index+1 . " fehlt"; + + return empty($error); + } + + /** + * rechnet nach ob der Virtuelle Gesamtbetrag (berechnet javascript) stimmt + */ + public function _ruleCalcAmount(string $value, array $data, ?string &$error, string $field){ + $subamounts = 0.0; + foreach ($data['input'] as $i => $inp){ + $subamounts = $subamounts + round(floatval($inp),2) - round(floatval($data['output'][$i]),2); + } + + if (round($value,2) == round($subamounts,2)) + return true; + else + $error = "Systemfehler: Amount Client ". floatval($value) . " ungleich Server Amount ". floatval($subamounts) ." Kalkulation; Diff = ".floatval($value)-floatval($subamounts); + + + return empty($error); + } + + public function _ruleCalcTotal(string $value, array $data, ?string &$error, string $field){ + log_message('debug',"Bills::_ruleCalcTotal In: ".$data['total_in']."; Out: ".$data['total_out']); + + if ((is_numeric($data['total_in']) && (round($data['total_in'],2) > 0.0)) && (is_numeric($data['total_out']) && (round($data['total_out'],2) > 0.0))) { + $error = "Systemfehler: Gesamtbetrag darf nur Input oder Output sein"; + // } else if ((!is_numeric($data['total_in'])) && (!is_numeric($data['total_out']))) { + // $error = "Systemfehler: Gesamtbetrag muss angegeben sein"; + } else { + $total = round(floatval($data['total_in'])-floatval($data['total_out']),2); + if (($total !== round(floatval($data['amount']),2)) && (round(floatval($data['openamount']),2) == 0.0)) + $error = "Systemfehler: Gesamtbetrag entspricht nicht Teilbeträgen"; + } + log_message('debug',"Bills::_ruleCalcTotal Ergebnis ".$error); + return empty($error); + } + + public function checkBillForm(){ + $rules = [ + 'id'=>['required','is_natural'], + 'source_id' => ['label'=>'Konto','rules'=>['required','greater_than[0]'],'errors'=>['greater_than'=>'Konto fehlt']], + 'datum' => 'required', + 'scheduled' => 'required', + 'transfer' => 'required', + 'sid.*' => ['label'=>'BuchungszeileID','rules'=>['required','is_natural']], + 'receiver' => ['label'=>'Empfänger','rules'=>['required','max_length[120]'],'errors'=>['required'=>'Empfänger fehlt']], + 'account_id.*' => [[$this,'_ruleBookingLineAcc']], + 'input.*' => [[$this,'_ruleBookingLineAmount']], + 'comment.*' => ['label'=>'Kommentar','rules'=>['max_length[150]']], + 'output.*' => ['permit_empty','numeric'], + 'amount' => ['required', 'numeric', [$this,'_ruleCalcAmount']], + 'billfile' => ['max_size[billfile,10000]','mime_in[billfile,application/pdf,image/jpg,image/jpeg]'], + 'removeBillFile' => ['max_length[50]'], + 'validate' => ['required'], + 'scheduledNum' =>['required_with[scheduled]','permit_empty','numeric'], + 'scheduledType' =>['required_with[scheduled]','permit_empty','in_list[week,month,year]'] + ]; + if ( 1 < count($this->request->getPost('sid')) ) { + $rules['openamount'] = ['rules'=>['numeric','equals[0.00]'], 'errors'=>['equals'=>'Restbetrag muss 0 sein']]; + $rules['total_in'] = [[$this,'_ruleCalcTotal']]; + $rules['total_out'] = ['required_without[total_in]','permit_empty','numeric']; + } + + $data = $this->request->getPost();// filter getPost by array_keys($rules) but indexed rules not working + log_message('debug','Bills::checkBillForm: Form Data: '.json_encode($data)); + + if (! $this->validateData($data, $rules)){ + log_message('debug','Bills::checkBillForm: validation failed: '.json_encode($data)); + log_message('debug','Bills::checkBillForm: validation failed: '.json_encode($this->validator->getErrors())); + $eBooking = new \App\Entities\eBooking($data); + return $this->viewBill($eBooking, $this->validator->getErrors()); +// return redirect()->back()->withInput()->with('errors', $validation->getErrors()); + } + else{ + log_message('debug','Bills::checkBillForm: validation success: '.json_encode($this->validator->getValidated())); + $eBooking = new \App\Entities\eBooking($this->validator->getValidated()); + if ($data['scheduled'] == "0"){ + $file = $this->request->getFile('billfile'); + if ($file) { + if (! $file->isValid() && ($file->getError() != UPLOAD_ERR_NO_FILE)) { + throw new \RuntimeException($file->getErrorString() . '(' . $file->getError() . ')'); + } + $eBooking->setInvoice($file); + } + + } + + $result = $eBooking->saveIt(); + if (true !== $result) // show error of database + $this->viewBill($eBooking, $result); + + $redirectURL = session('redirect_url') ?? site_url('/'); + unset($_SESSION['redirect_url']); + return redirect()->to($redirectURL); + } + } + + public function viewBill(\App\Entities\eBooking $fdata, $errors = NULL){ + helper('form'); + log_message('debug','Bills::viewBill: fdata: '.json_encode($fdata->toArray())); + + $data['booking'] = $fdata->getFormData(); + + $data['validation'] = $errors; + return view('newBill', $data); + } + + public function viewInvoice(string $filename) { + $filePath= WRITEPATH . '/uploads/'.auth()->user()->preFixDB; + $file = new \CodeIgniter\Files\File($filePath."/".$filename); + log_message('debug',$file->getMimeType()); + if ($file->getMimeType() == 'application/pdf') + $this->viewPdf($filename); + else{ + $imginfo = getimagesize($filePath."/".$filename); + header("Content-type: {$imginfo['mime']}"); + readfile($filePath."/".$filename); + exit; + } +// return view('viewBillImg', ['filename'=>'writable/uploads/'.$filename]); + } + + + private function viewPdf(string $filename) { + // $view = \Config\Services::renderer(); + log_message('debug',$filename); + $filePath= WRITEPATH . '/uploads/'.auth()->user()->preFixDB; + header("Cache-Control: maxage=1"); + header("Pragma: public"); + header('Content-type:application/pdf'); + header('Content-disposition: inline; filename="'.$filename.'"'); + header('content-Transfer-Encoding:binary'); + header('Content-Length:' .filesize($filePath."/".$filename)); + if (($pdf =file_get_contents($filePath."/".$filename)) === false) + print $filePath."/".$filename; + else + print $pdf; + exit; + } + +} diff --git a/app/Controllers/Home.php b/app/Controllers/Home.php index 76869d7..94b0103 100644 --- a/app/Controllers/Home.php +++ b/app/Controllers/Home.php @@ -1,352 +1,28 @@ session = service('session'); - $this->accounts = model('App\Models\mAccounts'); + } - public function overview() - { + public function overview() { + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $bills = model('App\Models\mBills'); /* @var \App\Models\mBills $bills */ + $billDet = model('App\Models\mBillDetails'); /* @var \App\Models\mBills $billDet */ + $scheduled = model('App\Models\mScheduled'); /* @var \App\Models\mScheduled $scheduled */ return view('table'); } - - private function checkDDPList(&$arr, $ele){ - - if (!array_key_exists($ele, $arr)){ - $arr[$ele] = $this->accounts->getDropDownEntry($ele); - } - } - - private function checkDDSList($arr, $par, $ele){ - if (!array_key_exists($par, $arr)){ - $ret = array(); - if ($ele > 0) - $ret[$ele] = $this->accounts->getDropDownEntry($ele); - else - $ret[0] = '-----'; - return $ret; - } - else{ - return $arr[$par]; - } - - } - - private function loadFormDefaults(){ - $data['id'] = 0; - $data['source'] = 0; - $data['datum'] =''; - $data['renummer'] = false; - $data['multi'] = false; - $data['transfer'] = false; - $data['receiver'] = ''; - $data['total_in'] = ''; - $data['total_out'] = ''; - $data['validate'] = false; - $data['scheduled'] = false; - $data['scheduledNum'] = 0; - $data['scheduledType'] = 0; - for ($i=0;$i<10;$i++){ - $data['sid'][$i] = 0; - $data['category_parent'][$i] = 0; - $data['category'][$i] = 0; - $data['input'][$i] = ''; - $data['output'][$i] = ''; - $data['comment'][$i] = ''; - } - return $data; - } - - /** - * Legt eine neue Terminbuchung an - * @param bool $transfer ob Terminbuchung vom Typ Transfer ist - * @return char gibt die View zurück - **/ - public function newScheduled($transfer = 0){ - $data = $this->loadFormDefaults(); - $data['transfer'] = (bool)$transfer; - return $this->newBill($data,NULL,true); - } - - public function newTransfer(){ - $data = $this->loadFormDefaults(); - $data['transfer'] = true; - return $this->newBill($data,NULL); - } - - public function newBill($data = NULL, $errors = NULL,$scheduled = false){ - helper('form'); - $data = $data ?? $this->loadFormDefaults(); - $data['isScheduled'] = $scheduled; - $data['validation'] = $errors; - - $srclist = $this->accounts->getDropDownList( 'Acc', TRUE ); - $this->checkDDPList($srclist,$data['source']); - $data['sourcelist'] = $srclist; - $data['scheduled'] = array('NumList' => array('1'=>'1','2'=>'2','3'=>'3','4'=>'4','5'=>'5','6'=>'6'), - 'TypeList' => array('week'=>'Woche(n)','month'=>'Monat(e)','year'=>'Jahr(e)'), - 'style' => 'class="form-select"'); - if ($data['transfer']) - $categories = $this->accounts->getDropDownLists( 'Acc', TRUE ); - else - $categories = $this->accounts->getDropDownLists( 'InOut', TRUE ); - - // $data['catparentlist'] = $categories['parent']; - // if (!array_key_exists('catparent', $data)) - // $data['catparent'] = $this->accounts->getParent($data['cat']); - // $this->checkDDPList($data['catparentlist'],$data['catparent']); - - // $data['catlist'] = $this->checkDDSList((array)$categories['subs'],$data['catparent'],$data['cat']); - - $data['subsparent'] = array(); - $data['subscategory'] = array(); - for ($i=0;$i<10;$i++){ - $data['subsparent'][$i] = $categories['parent']; - $data['category_parent'][$i] = $data['category_parent'][$i] ?? 0; - $this->checkDDPList($data['subsparent'][$i],$data['category_parent'][$i]); - $data['subscategory'][$i] = $this->checkDDSList((array)$categories['subs'],$data['category_parent'][$i],$data['category'][$i]??0); - } - $data['subsdata'] = json_encode($categories['subs']); - return view('newBill', $data); - } - - private function customValidation(&$errors){ - $result = true; -// if ( "on" == $this->request->getPost('multi') ) -// { - for ($i=0;$i<10;$i++){ - $inp = $this->request->getPost("input[$i]"); - $outp = $this->request->getPost("output[$i]"); - $cat = $this->request->getPost("category[$i]"); - if ((is_numeric($inp) && $inp > 0.0) || (is_numeric($outp) && $outp > 0.0)){ - if ($cat == 0){ - $result &= false; - $errors['Kategorie']="Kategorie [$i] fehlt oder ungültig"; - } - } - if ($cat > 0){ - $errors['log2'] = "cat recognized"; - if (!((is_numeric($inp) && ($inp > 0.0)) || (is_numeric($outp) && ($outp > 0.0)))){ - $errors['Betrag']="Betrag [$i] fehlt oder ungültig"; - $result &= false; - } - } - } -// } - // else{ - // if ((is_numeric($this->request->getPost('total_in')) && $this->request->getPost('total_in') > 0.0) || (is_numeric($this->request->getPost('total_out')) && $this->request->getPost('total_out') > 0.0)){ - // return true; - // } - // else - // $errors['Betrag']="Betrag fehlt oder ungültig"; - // } - return $result; - } - - public function viewBill(string $filename) { - $filePath= WRITEPATH . '/uploads'; - $file = new \CodeIgniter\Files\File($filePath."/".$filename); - log_message('debug',$file->getMimeType()); - if ($file->getMimeType() == 'application/pdf') - $this->viewPdf($filename); - else{ - $imginfo = getimagesize($filePath."/".$filename); - header("Content-type: {$imginfo['mime']}"); - readfile($filePath."/".$filename); - exit; - } -// return view('viewBillImg', ['filename'=>'writable/uploads/'.$filename]); - } - - - public function viewPdf(string $filename) { - $view = \Config\Services::renderer(); - log_message('debug',$filename); - $filePath= WRITEPATH . '/uploads'; - header("Cache-Control: maxage=1"); - header("Pragma: public"); - header('Content-type:application/pdf'); - header('Content-disposition: inline; filename="'.$filename.'"'); - header('content-Transfer-Encoding:binary'); - header('Content-Length:' .filesize($filePath."/".$filename)); - if (($pdf =file_get_contents($filePath."/".$filename)) === false) - print $filePath."/".$filename; - else - print $pdf; - exit; - } - - public function attemptNewBilling() - { - helper('form'); - $uploading = false; - $rules = [ - 'source' => 'required|greater_than[0]', - 'datum' => 'required', - 'category.0' => 'required|greater_than[0]' -// 'totalamount'=>'required|numeric' - ]; - $errors = [ // Errors - 'source' => ['required' => 'Konto fehlt','greater_than' => 'Konto fehlt'], - 'datum' => ['required' => 'Datum fehlt']]; -// 'totalamount' => ['required' => 'Betrag fehlt','numeric' => 'Betrag muss eine Zahl sein']]; - if ( 'on' == $this->request->getPost('multi') ) { - $rules['openamount'] = 'equals[0]'; - $errors['openamount'] = ['equals' => 'Betrag muss 0 sein']; - } - $fehler= array(); - $valid = $this->customValidation($fehler); - $file = $this->request->getFile('billfile'); - if ($file) { - if (! $file->isValid() && ($file->getError() != UPLOAD_ERR_NO_FILE)) { - throw new \RuntimeException($file->getErrorString() . '(' . $file->getError() . ')'); - } else if ($file->isValid()) { - $rules['billfile'] = ['uploaded[billfile]','max_size[billfile,10000]','mime_in[billfile,application/pdf,image/jpg,image/jpeg]']; - $errors['billfile'] = ['uploaded'=>'Fehler beim Upload','max_size' => 'Zu Groß', 'mime_in' => "Ungültiger Datentyp"]; - $uploading = true; - } - } - if ((! $this->validate($rules,$errors)) || (!$valid)){ - return $this->newBill($this->request->getPost(), array_merge($this->validator->getErrors(),$fehler),$this->request->getPost('scheduled')); -// return redirect()->back()->withInput()->with('errors', $validation->getErrors()); - } - else{ - $data = $this->request->getPost(); - log_message('debug',print_r($data,true)); - - if ($this->request->getPost('scheduled') =="0"){ - $bills = model('App\Models\mBills'); - if ($this->request->getPost('removeBillFile')){ - $bills->deleteBillFile($data['id']); - unlink(WRITEPATH . 'uploads/'.$this->request->getPost('removeBillFile')); - } - $billId = $bills->saveBill($data); - - if ($uploading){ - $date = Time::parse($data['datum']); - $file->move(WRITEPATH . 'uploads',$date->toLocalizedString('yyyyMMdd')." ".$data['receiver']." ".$billId.".".$file->guessExtension()); - log_message('debug', $file->getName()); - $bills->storeBillFile($billId, $file->getName()); - } - - $redirectURL = session('redirect_url') ?? site_url('/'); - unset($_SESSION['redirect_url']); - return redirect()->to($redirectURL); - } - else{ - $scheduled = model('App\Models\mScheduled'); - $scheduled->saveBill($data); - } - $this->response->redirect(site_url('/')); - } - } - - public function editBill($id){ - $bills = model('App\Models\mBills'); - $data = $this->loadFormDefaults(); - $result = $bills->getEntry($id, $data, $this->accounts); - return $this->newBill($result); - } - - public function editScheduled($id){ - $scheduled = model('App\Models\mScheduled'); - $entry = $scheduled->getEntry($id); - // $bill = array_merge($this->loadFormDefaults(),(array)json_decode($entry['data'])); - // $bill['datum'] = $entry['next_date']; - return $this->newBill($entry,NULL,true); - } - - public function deleteScheduled($id){ - $scheduled = model('App\Models\mScheduled'); - $entry = $scheduled->getEntry($id); - // $bill = array_merge($this->loadFormDefaults(),(array)json_decode($entry['data'])); - // $bill['datum'] = $entry['next_date']; - return $this->newBill($entry,NULL,true); - } - - public function syncScheduled(){ - if (!$this->request->isAJAX()) return; - $scheduled = model('App\Models\mScheduled'); - $schedules = $scheduled->findAll(); - $result = array(); - $line = ""; - foreach ( $schedules as $schedule ) - { - $cur_date = $schedule['next_date']; - while ( strtotime( $cur_date ) <= strtotime( "now" ) ) - { - $line .= " $cur_date - "; - $bill = array_merge($this->loadFormDefaults(),(array)json_decode($schedule['data'])); - //echo "Hugo:".$schedule['id']; - $bill['id']=0; // to generate a new booking - $bill['datum'] = $cur_date; - $bill['type'] = $bill['transfer']?'transfer':'multiple'; - $line .= $bill['receiver']; - $bills = model('App\Models\mBills'); - $bills->saveBill($bill); - $cur_date = $scheduled->calcNextDate( $cur_date, $bill['scheduledNum'], $bill['scheduledType']); - $line .= " - $cur_date
"; - //log_message('debug','BudgetScheduled:process data: '.logArray($booking->getData())); - $scheduled->update($schedule['id'], ['next_date'=>$cur_date ]); - - } - } - return $line; - //$this->showTableBookings(); - } - - public function viewAccountsTree(){ - helper('form'); - $data = $this->accounts->getTreeData(); - $data['ddlist'] = $this->accounts->getDropDownList( 'all', false,true ); - return view('accountstree',$data); - } - - public function attemptEditAccount(){ - $rules = [ - 'description' => 'required', - 'type' => 'required', - ]; - if (! $this->validate($rules)) - { - return redirect()->back()->withInput()->with('errors', $this->validator->getErrors()); - } - $this->accounts->saveAccount($this->request->getPost()); -// $this->accounts = model('App\Models\mAccounts', false); - return $this->viewAccountsTree(); - - } - - public function viewAccountActivities(){ - helper('form'); - $data = array(); - $data['source'] = 0; - $data['start'] = session('datum_start') ?? date("d.m.Y", strtotime( "- 2 weeks", strtotime( "now" ) )); - $data['ende'] = session('datum_ende') ?? date("d.m.Y", strtotime( "now" ) ); - $data['source'] = session('source') ?? 0; - $data['sourcelist'] = $this->accounts->getDropDownList( 'all', TRUE ); - return view('activities', $data); - } - - public function viewScheduled(){ - helper('form'); - return view('scheduled'); - } } diff --git a/app/Controllers/Scheduled.php b/app/Controllers/Scheduled.php new file mode 100644 index 0000000..b72d96b --- /dev/null +++ b/app/Controllers/Scheduled.php @@ -0,0 +1,83 @@ +session = service('session'); + } + + public function new(bool $transfer=false){ + //log_message('debug','Bills::new'); + $eBooking = new \App\Entities\eBooking(); + $eBooking->transfer = $transfer; + $eBooking->scheduled = true; + $eBooking = $eBooking->getNew(); + return $this->viewBill($eBooking); + } + + public function edit(int $id){ + $scheduled = model('App\Models\mScheduled'); + $entry = $scheduled->getEntry($id); + $eBooking = new \App\Entities\eBooking($entry); + return $this->viewBill($eBooking); + } + + public function sync(){ + if (!$this->request->isAJAX()) return; + $scheduled = model('App\Models\mScheduled'); + $schedules = $scheduled->findAll(); + $lines = ""; + foreach ( $schedules as $schedule ) { + $cur_date = $schedule['next_date']; + while ( strtotime( $cur_date ) <= strtotime( "now" ) ) { + $data = (array)json_decode($schedule['data']); + $data['id'] = 0; // to generate a new booking (overwrite id of scheduled DB) + $data['scheduled'] = false; + $eBooking = new \App\Entities\eBooking($data); + $eBooking->datum = $cur_date; + $lines .= " $cur_date - $eBooking->receiver
"; + $eBooking->saveIt(); + $cur_date = $scheduled->calcNextDate( $cur_date, $data['scheduledNum'], $data['scheduledType']); + } + $scheduled->update($schedule['id'], ['next_date'=>$cur_date ]); + } + return $lines; + } + + public function viewScheduled(){ + helper('form'); + return view('scheduled'); + } + + public function delete(){ + if ($this->request->is('post')) { + log_message('debug',"delete id: ".$this->request->getPost("id")); + if (!ctype_digit($this->request->getPost("id"))) return $this->failForbidden("Ungültige ID"); + $schmodel = model(mScheduled::class); + $result = $schmodel->delete(intval($this->request->getPost("id"))); + if ($result !== true) + return $this->fail("Database error ".$result); + else + return $this->respondDeleted(['url'=>session('redirect_url') ?? site_url('/'),'ID'=>$this->request->getPost("id")]); + } + return $this->failUnauthorized("Schäm Dich"); + } + + public function getTabData(){ + if (!$this->request->isAJAX()) return; + $scheduled = model('App\Models\mScheduled'); + return json_encode($scheduled->getEntries()); + } + +} \ No newline at end of file diff --git a/app/Entities/eBooking.php b/app/Entities/eBooking.php new file mode 100644 index 0000000..5963ac3 --- /dev/null +++ b/app/Entities/eBooking.php @@ -0,0 +1,194 @@ + 0, + 'datum' => '', + 'source_id' => 0, + 'receiver' => '', + 'amount' => null, + 'validate' => false, + 'transfer' => false, + 'scheduled' => false, + 'details' => [], + 'deleted_at' => null, + 'rechnung' => null, + 'scheduledNum' => null, + 'scheduledType' => null + ]; + + public function fillDetails(){ + $billD = model(mBillDetails::class); + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $this->attributes['details'] = $billD->where('bill_id',$this->attributes['id'])->findAll(); + $this->attributes['scheduled'] = false; + $this->attributes['transfer'] = ((count($this->attributes['details']) == 1) && $accounts->isAccount($this->attributes['details'][0]->account_id)); + return $this; + } + + public function setInvoice(\CodeIgniter\HTTP\Files\UploadedFile $file){ + $this->file = $file; + } + + public function getNew(){ + $this->attributes['datum'] = date('Y-m-d'); + $this->attributes['details'][] = new \App\Entities\eBookingLine(); + log_message('debug','eBooking::getNew: '.json_encode($this->attributes)); + return $this; + } + + public function getFormData(){ + $billDet = new \App\Entities\eBookingLine(); + $data = $this->toArray(); + $data['template'] = $billDet->toArray(); + log_message('debug','eBooking::getFormData:template '.json_encode($data['template'])); + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $data['category'] = $accounts->getDropDownLists($this->attributes['transfer']?'Acc':'InOut'); + $data['sourcelist'] = $accounts->getDropDownList( type:'Acc', defaultKey:$data['source_id'] ); +// log_message('debug','eBooking::getFormData: '.json_encode($data)); + //$billDet->createDropDownLists($data); + + if (is_null($this->attributes['amount'])){ + $data['total_in'] = ''; + $data['total_out'] = ''; + } + else { + $data['total_in'] = ($this->attributes['amount'] >= 0.0)? abs($this->attributes['amount']):''; + $data['total_out'] = ($this->attributes['amount'] < 0.0)? abs($this->attributes['amount']):''; + } + + if ($this->attributes['scheduled']??false){ + // $data['scheduledNum'] = 0; + // $data['scheduledType'] = 0; + $data['schedule_lists'] = array('NumList' => array('1'=>'1','2'=>'2','3'=>'3','4'=>'4','5'=>'5','6'=>'6'), + 'TypeList' => array('week'=>'Woche(n)','month'=>'Monat(e)','year'=>'Jahr(e)')); + } + + if (count($this->attributes['details'])>0){ + foreach ($this->attributes['details'] as $i => $line){ + $line->createFormData($data,$i); + } + }else{ + foreach ($this->attributes['sid'] as $i => $line){ + $billDet->createDropDownLists($data,$i); + } + } + log_message('debug','eBooking::getFormData: '.json_encode($data)); + return $data; + } + + // public function convertFromPost(array $data){ + // $this->fill($data); + // foreach ($data['sid'] as $i => $value){ + // $sub = array('id' => $value, + // 'bill_id' => $data['id'], + // 'comment' => $data['comment'][$i], + // 'account_id' => $data['account_id'][$i], + // 'subamount' => floatval($data['input'][$i])-floatval($data['output'][$i]), + // 'deleted_at' => null); + // $details[] = new \App\Entities\eBookingLine($sub); + // } + // } + + private function calcAmount(){ + $amount = floatval($data['total_in'])-floatval($data['total_out']); + $subamounts = 0.0; + foreach ($this->attributes['input'] as $i => $value){ + $subamounts = $subamounts + floatval($value) - floatval($this->attributes['output'][$i]); + } + } + + private function saveScheduled() { + $scheduledModel = model(mScheduled::class); + $data = $this->toArray(); + for ($i=(count($data['account_id'])-1); $i>0; $i--){ + if ($data['account_id'][$i] == "0") { + unset($data['account_id'][$i]); + unset($data['sid'][$i]); + unset($data['input'][$i]); + unset($data['output'][$i]); + unset($data['comment'][$i]); + continue; + } + } + + return $scheduledModel->saveIt($data); + + } + //switch scheduled or bill + + public function saveIt() { + if ($this->attributes['scheduled'] != 0) return $this->saveScheduled(); + + $billModel = model(mBills::class); + + if ($this->attributes['id'] == 0) { + + $billId = $billModel->insert($this, true); + log_message('debug','SaveIt with insert result: '.json_encode($billId)); + if ($billId === false) + return $billModel->errors(); + else{ + $this->attributes['id'] = $billId; + } + //$billId = $$billModel->getInsertID(); + } else { + if ($this->attributes['removeBillFile']??false){ + $this->attributes['rechnung'] = null; + unlink(WRITEPATH . 'uploads/'.auth()->user()->preFixDB.'/'.$this->attributes['removeBillFile']); + } + if ($billModel->update($this->attributes['id'],$this) === false) + return $billModel->errors(); + } + + $billDetailsModel = model(mBillDetails::class); + $details = new \App\Entities\eBookingLine; + foreach ($this->attributes['sid'] as $i => $value){ + if ($this->attributes['account_id'][$i] == 0) { // leere Zeile ignorieren bzw wenn id (also bereits in DB) dann löschen + if ($value == 0) { + continue; + } else { + $billDetailsModel->delete($value); + continue; + } + } + $sub = array('sid' => ($value == 0)? NULL: $value, + 'bill_id' => $this->attributes['id'], + 'comment' => $this->attributes['comment'][$i], + 'account_id' => $this->attributes['account_id'][$i], + 'subamount' => floatval($this->attributes['input'][$i])-floatval($this->attributes['output'][$i]), + 'deleted_at' => null); + $details->fill($sub); + $billDetailsModel->save($details); + } + + if ($this->file) { + if (! $this->file->isValid() && ($this->file->getError() != UPLOAD_ERR_NO_FILE)) { + throw new \RuntimeException($this->file->getErrorString() . '(' . $this->file->getError() . ')'); + } else if ($this->file->isValid()) { + helper('url'); + $date = Time::parse($this->attributes['datum']); + $name = url_title($date->toLocalizedString('yyyyMMdd')." ".$this->attributes['receiver']." ".$this->attributes['id']); + $this->file->move(WRITEPATH . 'uploads/'.auth()->user()->preFixDB,$name.".".$this->file->guessExtension()); + log_message('debug', $this->file->getName()); + $this->attributes['rechnung'] = $this->file->getName(); + if ($billModel->update($this->attributes['id'],$this) === false) + return $billModel->errors(); + } + } + + // $details = model('App\Models\mBillDetails'); + // return $details->getDetails([$this->id]); + + return true; + } +} \ No newline at end of file diff --git a/app/Entities/eBookingLine.php b/app/Entities/eBookingLine.php new file mode 100644 index 0000000..98eeafd --- /dev/null +++ b/app/Entities/eBookingLine.php @@ -0,0 +1,69 @@ + 0, + 'bill_id' => 0, + 'comment' => '', + 'account_id' => 0, + 'subamount' => null, + 'deleted_at' => null, + ]; + + protected $datamap = [ + // property_name => db_column_name + 'sid' => 'id', + ]; + private function checkCreateParentDefault(array &$list, int $parentId) : void { + if ($parentId == 0) return; + if (!array_key_exists($parentId, $list)){ + log_message('debug',"Parent not exists: $parentId in List: ".json_encode(array_keys($list))); + $list[$parentId] = $this->accounts->getDropDownEntry($parentId); + } + } + + private function checkCreateSubDefault(array &$list, int $accountId) : void { + if (!array_key_exists($accountId, $list)){ + if ($accountId > 0) + $list[$accountId] = $this->accounts->getDropDownEntry($accountId); + else + $list[0] = '-----'; + } + } + + public function createFormData(array &$data, int $i){ + if (!array_key_exists('sid', $data)) $data['sid'] = []; + if (!array_key_exists('comment', $data)) $data['comment'] = []; + if (!array_key_exists('account_id', $data)) $data['account_id'] = []; + if (!array_key_exists('deleted_at', $data)) $data['deleted_at'] = []; + if (!array_key_exists('input', $data)) $data['input'] = []; + if (!array_key_exists('output', $data)) $data['output'] = []; + + $data['sid'][$i] = $this->attributes['id']; + $data['comment'][$i] = $this->attributes['comment']; + $data['account_id'][$i] = $this->attributes['account_id']; + $data['deleted_at'][$i] = $this->attributes['deleted_at']; + $data['input'][$i] = (!is_null($this->attributes['subamount']) && $this->attributes['subamount'] > 0.0) ? $this->attributes['subamount'] :''; + $data['output'][$i] = (!is_null($this->attributes['subamount']) && $this->attributes['subamount'] <= 0.0) ? abs($this->attributes['subamount']) :''; + $this->createDropDownLists($data,$i); + } + + public function createDropDownLists(array &$data, int $i){ + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $lists = $data['category'] ?? []; + if (!array_key_exists('parent_id', $data)) $data['parent_id'] = []; + if (!array_key_exists('cat_parent', $data)) $data['cat_parent'] = []; + if (!array_key_exists('cat_sub', $data)) $data['cat_sub'] = []; + + $data['parent_id'][$i] = $accounts->getParent($data['account_id'][$i]); + $data['cat_parent'][$i] = $lists['parent']; + $this->checkCreateParentDefault($data['cat_parent'][$i],$data['parent_id'][$i]); + $data['cat_sub'][$i] = $lists['subs']->{$data['parent_id'][$i]} ?? []; + $this->checkCreateSubDefault($data['cat_sub'][$i],$data['account_id'][$i]); + } +} \ No newline at end of file diff --git a/app/Language/de/Validation.php b/app/Language/de/Validation.php new file mode 100644 index 0000000..6abf8c2 --- /dev/null +++ b/app/Language/de/Validation.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +// Validation language settings +return [ + // Core Messages + 'noRuleSets' => 'No rule sets specified in Validation configuration.', + 'ruleNotFound' => '"{0}" is not a valid rule.', + 'groupNotFound' => '"{0}" is not a validation rules group.', + 'groupNotArray' => '"{0}" rule group must be an array.', + 'invalidTemplate' => '"{0}" is not a valid Validation template.', + + // Rule Messages + 'alpha' => 'The {field} field may only contain alphabetical characters.', + 'alpha_dash' => 'The {field} field may only contain alphanumeric, underscore, and dash characters.', + 'alpha_numeric' => 'The {field} field may only contain alphanumeric characters.', + 'alpha_numeric_punct' => 'The {field} field may contain only alphanumeric characters, spaces, and ~ ! # $ % & * - _ + = | : . characters.', + 'alpha_numeric_space' => 'The {field} field may only contain alphanumeric and space characters.', + 'alpha_space' => 'The {field} field may only contain alphabetical characters and spaces.', + 'decimal' => 'The {field} field must contain a decimal number.', + 'differs' => 'The {field} field must differ from the {param} field.', + 'equals' => 'Das Feld {field} muss exakt sein: {param}.', + 'exact_length' => 'The {field} field must be exactly {param} characters in length.', + 'field_exists' => 'The {field} field must exist.', + 'greater_than' => 'The {field} field must contain a number greater than {param}.', + 'greater_than_equal_to' => 'The {field} field must contain a number greater than or equal to {param}.', + 'hex' => 'The {field} field may only contain hexadecimal characters.', + 'in_list' => 'The {field} field must be one of: {param}.', + 'integer' => 'The {field} field must contain an integer.', + 'is_natural' => 'The {field} field must only contain digits.', + 'is_natural_no_zero' => 'The {field} field must only contain digits and must be greater than zero.', + 'is_not_unique' => 'The {field} field must contain a previously existing value in the database.', + 'is_unique' => 'The {field} field must contain a unique value.', + 'less_than' => 'The {field} field must contain a number less than {param}.', + 'less_than_equal_to' => 'The {field} field must contain a number less than or equal to {param}.', + 'matches' => 'The {field} field does not match the {param} field.', + 'max_length' => '{field} darf nicht länger als {param} Zeichen sein.', + 'min_length' => 'The {field} field must be at least {param} characters in length.', + 'not_equals' => 'The {field} field cannot be: {param}.', + 'not_in_list' => 'The {field} field must not be one of: {param}.', + 'numeric' => 'Das Feld {field} darf nur Zahlen enthalten.', + 'regex_match' => 'The {field} field is not in the correct format.', + 'required' => 'Das Feld {field} ist erforderlich.', + 'required_with' => 'The {field} field is required when {param} is present.', + 'required_without' => 'Das Feld {field} muss ausgefüllt werden, wenn {param} nicht vorhanden ist.', + 'string' => 'The {field} field must be a valid string.', + 'timezone' => 'The {field} field must be a valid timezone.', + 'valid_base64' => 'The {field} field must be a valid base64 string.', + 'valid_email' => 'The {field} field must contain a valid email address.', + 'valid_emails' => 'The {field} field must contain all valid email addresses.', + 'valid_ip' => 'The {field} field must contain a valid IP.', + 'valid_url' => 'The {field} field must contain a valid URL.', + 'valid_url_strict' => 'The {field} field must contain a valid URL.', + 'valid_date' => 'The {field} field must contain a valid date.', + 'valid_json' => 'The {field} field must contain a valid json.', + + // Credit Cards + 'valid_cc_number' => '{field} does not appear to be a valid credit card number.', + + // Files + 'uploaded' => '{field} is not a valid uploaded file.', + 'max_size' => '{field} is too large of a file.', + 'is_image' => '{field} is not a valid, uploaded image file.', + 'mime_in' => '{field} does not have a valid mime type.', + 'ext_in' => '{field} does not have a valid file extension.', + 'max_dims' => '{field} is either not an image, or it is too wide or tall.', + 'min_dims' => '{field} is either not an image, or it is not wide or tall enough.', +]; diff --git a/app/Models/Cast/CastMyDate.php b/app/Models/Cast/CastMyDate.php new file mode 100644 index 0000000..34dabd4 --- /dev/null +++ b/app/Models/Cast/CastMyDate.php @@ -0,0 +1,49 @@ +dateFormat['date']; + + return Time::createFromFormat($format, $value)->toLocalizedString($params[0] ?? 'yyyy-MM-dd'); + } + + public static function set( mixed $value, array $params = [], ?object $helper = null,): string { + if ( is_string($value)) { + $value = Time::createFromFormat("Y-m-d", $value); + } + if (! $value instanceof Time) { + self::invalidTypeValueError($value); + } + + if (! $helper instanceof BaseConnection) { + $message = 'The parameter $helper must be BaseConnection.'; + + throw new InvalidArgumentException($message); + } + + $format = $helper->dateFormat['date']; + + return $value->format($format); + } +} \ No newline at end of file diff --git a/app/Models/mAccounts.php b/app/Models/mAccounts.php index 24e28be..b6b87a8 100644 --- a/app/Models/mAccounts.php +++ b/app/Models/mAccounts.php @@ -3,41 +3,65 @@ use CodeIgniter\Model; class mAccounts extends Model { - protected $table = 'budget_accounts'; + private $defaultTableName='_budget_accounts'; protected $primaryKey = 'id'; - + protected $useAutoIncrement = true; protected $returnType = 'object'; - protected $useSoftDeletes = false; + protected $useSoftDeletes = true; + protected $deletedField ="deleted_at"; - protected $allowedFields = ['parent_id','type','bookable','description','lkz']; + protected $allowedFields = ['parent_id','bookable','description','account','monitor','deleted_at']; protected $useTimestamps = false; - private $mAccountList = array(); + private ?array $mAccountList = null; - protected function initialize() - { - $result = $this->findAll(); - foreach ($result as $value) { - $this->mAccountList[$value->id] = $value; + protected array $casts = [ + 'bookable' => 'boolean', + 'monitor' => 'boolean', + 'account' => 'boolean', + ]; + + protected function initialize() { + log_message('debug','mAccounts initialize'); + $this->table = auth()->user()->preFixDB.$this->defaultTableName; + $this->checkOrCreateTable(); + + } + + /** + * Sicherstellen dass mAccountList gefüllt ist + */ + private function populateAccountList(): void { + if ($this->mAccountList === null) { + log_message('debug','mAccounts populateAccountList New'); + $result = $this->withDeleted()->findAll(); + // log_message('debug','mAccounts DB-Result '.json_encode($result)); + $this->mAccountList = []; + foreach ($result as $value) { + $this->mAccountList[$value->id] = $value; + } + //log_message('debug','mAccounts intern List '.json_encode($this->mAccountList)); } } - /** * Erstellt DropDownListe aus gegeben array - * @param array liste der daten - * @param bool wenn true wird auch ein 0-Eintrag erstellt + * @param array $data liste der daten + * @param bool $withnull wenn true wird auch ein 0-Eintrag erstellt + * @param int $key index für null eintrag * @return mixed */ - private function createDropDownList($data,$withnull = true,$key=0) - { + private function createDropDownList(array $data, bool $withnull = true, int $key=0) { $liste = array(); - if ($withnull) - $liste[$key] = '-----'; - foreach ( $data as $row ) - { + if ($withnull){ + if ($key > 0) + $liste[$key] = '-----'; + // else + // $liste[""] = '-----'; + } + foreach ( $data as $row ) { $liste[$row->id] = $row->description; } asort( $liste ); @@ -46,114 +70,114 @@ class mAccounts extends Model { /** * holt Text vom gegebenen Element - * @param int id des gewünschten Elements + * @param int $id ID des gewünschten Elements * @return string Full Name of Entry */ - public function getDropDownEntry($id){ + public function getDropDownEntry(int $id) { + $this->populateAccountList(); $element = $this->mAccountList[$id]; $description = $element->description; $id = $element->parent_id; - while ( $id > 0 ) - { + while ( $id > 0 ) { $description = $this->mAccountList[$id]->description .'-'.$description; $id = $this->mAccountList[$id]->parent_id; } - return $description;; + return $description; + } + + private function filterIt(string $type = 'all', bool $bookableOnly = true, bool $withDeleted = false, ?int $defaultKey = null){ + return unserialize(serialize(array_filter($this->mAccountList, function($var) use($type, $bookableOnly, $withDeleted, $defaultKey) { + if ($defaultKey !== null && $var->id == $defaultKey) + return true; + if (!($withDeleted) && $var->deleted_at) + return false; + if ($bookableOnly && !$var->bookable) + return false; + if ($type == 'Acc' && !$var->account) + return false; + elseif ($type == 'InOut' && $var->account) + return false; + + return true; + }))); } /** * liefert DropDown Liste mit gegeben Suchparametern - * @param string Kontotyp "InOut,In,Out,Acc" - * @param bool ob bookable gesetzt sein muss - * @return bool ob lkz Flag gesetzt sein darf + * @param string $type Kontotyp "InOut,Acc,all" + * @param bool $bookableOnly ob bookable gesetzt sein muss + * @param bool $withDeleted ob gelöschte auch + * @return array fertige DropDownListe */ - public function getDropDownList($type, $bookable = true, $lkz = false ) - { - if ($bookable == true) - $this->where('bookable',true); - if ($lkz == false) $this->where('lkz',NULL); - if ( $type == 'InOut' ) $this->where('type !=','account'); - elseif ( $type == 'Acc' ) $this->where('type','account'); - elseif ( $type == 'In' ) $this->where('type','input'); - elseif ( $type == 'Out' ) $this->where('type','output'); - $this->orderBy('description'); - $result = $this->findAll(); - $parent = array(); - foreach ( $result as &$element ) - { + public function getDropDownList(string $type, bool $bookableOnly = true, bool $withDeleted = false, ?int $defaultKey = null) : array { + $this->populateAccountList(); + $result = $this->filterIt($type,$bookableOnly,$withDeleted,$defaultKey); + foreach ( $result as &$element ) { $id = $element->parent_id; - while ( $id > 0 ) - { + while ( $id > 0 ) { $element->description = $this->mAccountList[$id]->description .'-'.$element->description; - if ($this->mAccountList[$id]->parent_id == 0){ - $parent[] = (object)['id' => $element->id,'description' => $element->description ]; - } $id = $this->mAccountList[$id]->parent_id; } } return $this->createDropDownList($result); } - /** + public function getCurDDLists(string $type, bool $bookableOnly = true, bool $withDeleted = false, ?int $defaultKey = null ) : array { + $this->populateAccountList(); + $list = $this->filterIt($type,$bookableOnly,$withDeleted, $defaultKey); + $parent = $this->getParent($defaultKey); + + } + + + /** * liefert 2 DropDown Listen mit gegeben Suchparametern * parent für Vorselektion, subs für Untereinträge - * @param string Kontotyp "InOut,In,Out,Acc" - * @param bool ob bookable gesetzt sein muss - * @return bool ob lkz Flag gesetzt sein darf + * @param string $type Kontotyp "InOut,In,Out,Acc" + * @param bool $bookableOnly ob bookable gesetzt sein muss + * @param bool $withDeleted ob lkz Flag gesetzt sein darf + * @return array fertige DropDownListen mit parent und subs */ - public function getDropDownLists($type, $bookable = true, $lkz = false ) - { - if ($bookable == true) $this->where('bookable',true); - if ($lkz == false) $this->where('lkz',null); - if ( $type == 'InOut' ) $this->where('type !=','account'); - elseif ( $type == 'Acc' ) $this->where('type','account'); - elseif ( $type == 'In' ) $this->where('type','output'); - elseif ( $type == 'Out' ) $this->where('type','input'); - $this->orderBy('description'); - $result = $this->findAll(); + public function getDropDownLists(string $type, bool $bookableOnly = true, bool $withDeleted = false, ?int $defaultKey = null ) : array { + $this->populateAccountList(); + $list = $this->filterIt($type,$bookableOnly,$withDeleted, $defaultKey); $parent = array(); $listen = array(); - foreach ( $result as &$element ) - { + foreach ( $list as &$element ) { $id = $element->parent_id; - while ( $id > 0 ) - { - if ($this->mAccountList[$id]->lkz == 1) continue 2; + while ( $id > 0 ) { + if ($this->mAccountList[$id]->deleted_at) continue 2; if ($this->mAccountList[$id]->parent_id == 0){ if (!array_key_exists($id, $parent)) $parent[$id] = (object)['id' => $id,'description' => $this->mAccountList[$id]->description ]; $listen[$id][$element->id] = (object)['id' => $element->id,'description' => $element->description ]; - } - else{ + } else{ $element->description = $this->mAccountList[$id]->description .'-'.$element->description; } $id = $this->mAccountList[$id]->parent_id; } } $subs = array(); - $subs[0] = $this->createDropDownList(array()); + $subs[0] = ["0"=>"----"]; foreach ( $listen as $key => $element ){ - $subs[$key] = $this->createDropDownList($element,($this->mAccountList[$key]->bookable=="t"), $key); + $subs[$key] = $this->createDropDownList($element,($this->mAccountList[$key]->bookable), $key); } return array('parent'=> $this->createDropDownList($parent),'subs'=>(object)$subs); } /** * liefert parent id für category - * @param int account id + * @param int $sid account id * @return int parent id */ - public function getParent($sid){ + public function getParent(int $sid): int{ if ($sid == 0) return 0; - $result = $sid; - $id = $this->mAccountList[$sid]->parent_id; - while ( $id > 0 ) - { - $result = $id; - $id = $this->mAccountList[$id]->parent_id; + $this->populateAccountList(); + while ( $this->mAccountList[$sid]->parent_id > 0 ) { + $sid = $this->mAccountList[$sid]->parent_id; } - return $result; + return $sid; } @@ -163,45 +187,71 @@ class mAccounts extends Model { */ public function getTreeData(){ helper('array'); - $parents = array(); - $subs = array(); - foreach ( $this->mAccountList as $element ) - { - if ($element->parent_id == 0) - $parents[$element->id] = $element; - } - foreach ( $this->mAccountList as $element ) - { - // if ($element->parent_id > 0) - // echo "".$parents[$element->parent_id]->id; + $this->populateAccountList(); + $parents = array_filter($this->mAccountList, function($var) { + return ($var->parent_id == 0); + }); + foreach ( $this->mAccountList as $element ) { if (($element->parent_id > 0) && array_key_exists($element->parent_id , $parents)) $parents[$element->parent_id]->subs[$element->id] = $element; } array_sort_by_multiple_keys($parents, ['description' => SORT_ASC]); - return ['parents'=>$parents,'subs'=>$subs, 'all'=>$this->mAccountList]; + return ['parents'=>$parents, 'all'=>$this->mAccountList]; } - /** + /** * erstellt oder updatet den gegeben Account * @var array formulardaten */ - public function saveAccount($data){ + public function saveAccount(array $data) : bool{ $this->set('parent_id',$data['parent']); - $this->set('type', $data['type']); - $this->set('bookable', array_key_exists('bookable', $data) ? true : false); + $this->set('bookable', array_key_exists('bookable', $data)); + $this->set('monitor', array_key_exists('monitor', $data)); + $this->set('account', array_key_exists('account', $data)); $this->set('description', $data['description']); - $this->set('lkz', (array_key_exists('lkz', $data) ? 1 : null)); + $this->set('deleted_at', (array_key_exists('deleted_at', $data) ? 'LOCALTIMESTAMP' : null),(array_key_exists('deleted_at', $data)? false: true)); if ($data['id'] == 0) - $this->insert(); + $result = $this->insert(); else - $this->update($data['id']); - $this->initialize(); + $result = $this->update($data['id']); + log_message('debug',$this->db->getLastQuery()); + return $result; } - public function checkType($id, $type){ - return ($this->mAccountList[$id]->type == $type); + /** + * prüft ob gegebene Kategorie ein Konot ist + * + * @param int $id + * @return bool + */ + public function isAccount(int $id){ + $this->populateAccountList(); + return ($this->mAccountList[$id]->account); } + + public function checkOrCreateTable(){ + log_message('debug',"Check or Create Account Table"); + if ($this->db->tableExists($this->table)) return; + $forge = \Config\Database::forge(); + $fields = array( + 'id' => array( 'type' => 'serial'), + 'parent_id' => array( 'type' => 'integer','default'=>'0'), + 'bookable' => array( 'type' => 'boolean' ), + 'description' => array( 'type' => 'character varying','constraint' => '80'), + 'monitor' => array( 'type' => 'boolean','default'=>'false' ), + 'account' => array( 'type' => 'boolean','default'=>'false' ), + 'deleted_at'=> array( 'type' => 'timestamp without time zone','null'=>true), + ); + $forge->addField( $fields ); + $forge->addPrimaryKey('id'); + + // $this->db->query("CREATE TABLE $this->table (LIKE $this->defaultTableName INCLUDING DEFAULTS INCLUDING CONSTRAINTS) PARTITION BY RANGE (datum);"); + $forge->createTable($this->table,true); + // CREATE TABLE temperatures_y2025q1 PARTITION OF temperatures + // FOR VALUES FROM ('2025-01-01') TO ('2025-04-01'); + } + }; ?> \ No newline at end of file diff --git a/app/Models/mBillDetails.php b/app/Models/mBillDetails.php index ef0b294..11e325c 100644 --- a/app/Models/mBillDetails.php +++ b/app/Models/mBillDetails.php @@ -3,31 +3,60 @@ use CodeIgniter\Model; class mBillDetails extends Model { - protected $table = 'budget_billdetails'; + private $defaultTableName='_budget_billdetails'; protected $primaryKey = 'id'; protected $useAutoIncrement = true; - protected $returnType = 'object'; - protected $useSoftDeletes = false; + protected $returnType = \App\Entities\eBookingLine::class; + protected $useSoftDeletes = true; + protected $deletedField ="deleted_at"; - protected $allowedFields = ['bill_id','comment','account_id','subamount','lkz']; + protected $allowedFields = ['bill_id','comment','account_id','subamount','deleted_at']; protected $useTimestamps = false; -// public function getEntries($id){ -// $this->select('mw_budget_bookingdetails.*,mw_budget_categories.description'); -// $this->join('mw_budget_categories', 'mw_budget_categories.id = mw_budget_bookingdetails.account_id', 'left'); -// $result = $this->orderBy('id','asc')->where('bill_id',$id)->findAll(); -// foreach ($result as $key => $value) { - -// // $value->datum = date( "d.m.Y", strtotime( $value->datum )); -// // $value->amount = number_format( $value->amount , 2, ',', '.' ); -// } -// return $result; -// } + protected array $casts = [ + 'subamount' => 'float', + 'account_id' => 'int', + 'bill_id' => 'int', + ]; - public function getDetails($ids){ + protected $validationRules = [ + 'bill_id' => 'required|numeric', + 'account_id' => 'required|numeric', + ]; + + protected $validationMessages = [ + 'bill_id' => [ + 'required' => 'Die Buchungsnummer muss angegeben sein.', + 'numeric' => 'Die Buchungsnummer muss eine Zahl sein.', + ], + 'account_id' => [ + 'required' => 'Die Kategorie muss angegeben sein.', + 'numeric' => 'Die Kategorie muss eine Zahl sein.', + ], + ]; + + protected function initialize() { + log_message('debug','mBillDetails initialize'); + $this->table = auth()->user()->preFixDB.$this->defaultTableName; + $this->checkOrCreateTable(); + } + + public function getEntries(int $id){ + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $this->select($this->table.'.*,'.$accounts->getTable().'.description'); + $this->join($accounts->getTable(), $accounts->getTable().'.id = '.$this->table.'.account_id', 'left'); + $result = $this->orderBy('id','asc')->where('bill_id',$id)->findAll(); + return $result; + } + + /** + * liefert für gegebene Buchung Details + * @param array $ids id der Buchung + */ + public function getDetails(array $ids): array { $quelle = model('App\Models\mAccounts'); $rows = $this->whereIn('bill_id', $ids)->findAll(); $result = array(); @@ -37,9 +66,18 @@ class mBillDetails extends Model { } return $result; } - public function getEntriesForForm($billId, &$result, $accounts){ - $this->select('budget_billdetails.*,budget_accounts.description'); - $this->join('budget_accounts', 'budget_accounts.id = budget_billdetails.account_id', 'left'); + + /** + * liefert für gegebene Buchung Details im Formularformat + * @param int $billId id der Buchung + * @param mixed $result Datenstruktur für Formular mit defaults + * @param \App\Models\mAccounts $accounts modelClass für Konten + * + */ + public function getEntriesForForm(int $billId, &$result){ + $accounts = model('App\Models\mAccounts'); + $this->select($this->table.'.*,'.$accounts->getTable().'.description'); + $this->join($accounts->getTable(), $accounts->getTable().'.id = '.$this->table.'.account_id', 'left'); $data = $this->orderBy('id','asc')->where('bill_id',$billId)->findAll(); $amount = 0.0; foreach ($data as $key => $row){ @@ -58,12 +96,12 @@ class mBillDetails extends Model { $result['total_in']= ($amount >= 0.0)?number_format( abs( $amount ), 2, ',', '' ):''; $result['total_out']= ($amount < 0.0)?number_format( abs( $amount ), 2, ',', '' ):''; } - else if ($accounts->checkType($result['category'][0],'account')) + else if ($accounts->isAccount($result['category'][0])) $result['transfer'] = true; return $result; } - public function saveEntries($data, $billId){ + public function saveEntries(array $data, int $billId){ for ($i=0;$i<10;$i++){ if ($data['category'][$i] > 0){ $this->set('subamount', floatval($data['input'][$i])-floatval($data['output'][$i])); //floatval($data['total_in'])-floatval($data['total_out'])); @@ -82,5 +120,28 @@ class mBillDetails extends Model { } } } + + public function checkOrCreateTable(){ + log_message('debug',"Check or Create BillDetails Table"); + if ($this->db->tableExists($this->table)) return; + log_message('debug',"Create BillDetails Table ".$this->table); + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $bills = model('App\Models\mBills'); /* @var \App\Models\mBills $bills */ + $forge = \Config\Database::forge(); + $fields = array( + 'id' => array( 'type' => 'serial'), + 'bill_id' => array( 'type' => 'integer'), + 'account_id' => array( 'type' => 'integer' ), + 'subamount' => array( 'type' => 'numeric','constraint' => '9,2' ), + 'comment' => array( 'type' => 'character varying','constraint' => '150'), + 'deleted_at'=> array( 'type' => 'timestamp without time zone','null'=>true), + ); + $forge->addField( $fields ); + $forge->addPrimaryKey('id'); + $forge->addForeignKey('bill_id', $bills->getTable(), 'id','NO ACTION','NO ACTION','bill_id_fk'); + $forge->addForeignKey('account_id', $accounts->getTable(), 'id','NO ACTION','NO ACTION','account_id_fk'); + $forge->createTable($this->table,true); + log_message('debug','Create BillDetails Table '.$this->db->lastQuery); + } }; ?> \ No newline at end of file diff --git a/app/Models/mBills.php b/app/Models/mBills.php index 4ae8369..0ff4c19 100644 --- a/app/Models/mBills.php +++ b/app/Models/mBills.php @@ -1,33 +1,58 @@ CastMyDate::class, + ]; + //protected $afterFind = ['checkTransfer']; + protected array $casts = [ + 'id' => 'int', + 'amount' => 'float', + 'datum' => 'myDate[dd.MM.yyyy]', + 'validate' => 'boolean', + 'source_id' => 'int', + ]; + + protected function initialize() { + log_message('debug','mBills initialize'); + $this->table = auth()->user()->preFixDB.$this->defaultTableName; + $this->checkOrCreateTable(); + } + + public function getTable() : string { + return $this->table; + } + // protected function checkTransfer(array $data){ + // $data['data']['transfer'] = ((count($this->attributes['details']) == 1) && $accounts->isAccount($this->attributes['details'][0]->account_id)); + // } /** - * liefert Buchungen für Aktivitätenliste + * liefert Buchungen für Aktivitätenliste für alte datentabelle * @return mixed Datensätze für Tabelle */ - public function getEntriesDet($start, $ende, $source){ + public function getEntriesDet(string $start, string $ende, int $source){ $details = model('App\Models\mBillDetails'); $quelle = model('App\Models\mAccounts'); $this->where('datum >=', date( 'Y-m-d', strtotime($start))); @@ -51,7 +76,7 @@ class mBills extends Model { } } - $result = $this->orderBy('datum','desc')->where('lkz',null)->findAll(); + $result = $this->orderBy('datum','desc')->findAll(); //->where('lkz',null) if (0 == sizeof($result)){ return $result; } @@ -64,7 +89,6 @@ class mBills extends Model { foreach ($result as $value) { $value->details = $ergebnis[$value->id]??null; - $value->datum = date( "d.m.Y", strtotime( $value->datum )); $value->source = $quelle->getDropDownEntry($value->source_id); // $value->amount = number_format( $value->amount , 2, ',', '.' ); } @@ -72,72 +96,44 @@ class mBills extends Model { } /** + * liefert Buchungen für Aktivitätenliste + * @return mixed Datensätze für Tabelle + */ + public function getActivities(string $start, string $ende, int $source){ + $quelle = model('App\Models\mAccounts'); + $this->where('datum >=', date( 'Y-m-d', strtotime($start))); + $this->where('datum <=', date( 'Y-m-d', strtotime($ende))); + if ($source > 0) { + $this->where('source_id', $source ); + } + $result = $this->orderBy('datum','desc')->findAll(); + if (0 == sizeof($result)) return $result; + foreach ($result as $value) { + $value->source = $quelle->getDropDownEntry($value->source_id); + } + return $result; + + } + /** * liefert Buchungen für Übersichtstabelle * @return mixed Datensätze für Tabelle */ - function getEntries(){ - $details = model('App\Models\mBillDetails'); - $quelle = model('App\Models\mAccounts'); - $result = $this->orderBy('datum','desc')->where('validate',false)->where('lkz',null)->findAll(10); - $keys=array(); - foreach ($result as $value) { - $keys[] = intval($value->id); - } - //$ergebnis = $details->whereIn('bill_id', $keys)->asArray()->findAll(); - $ergebnis = $details->getDetails($keys); - // print_r($ergebnis); - - foreach ($result as $value) { - $value->details = $ergebnis[$value->id]??null;//$details->getEntries($value->id); - $value->datum = date( "d.m.Y", strtotime( $value->datum )); - $value->quelle = $quelle->getDropDownEntry($value->source_id); -// $value->amount = number_format( $value->amount , 2, ',', '.' ); - } + public function getEntries(int $zeilen = 10){ + $result = $this->orderBy('datum','desc')->where('validate',false)->findAll($zeilen); //->where('lkz',null) + if (0 == sizeof($result)) return $result; return $result; - } - - /** - * liefert gegebene Buchung inkl. Details im Formularformat - * @param int id der Buchung - * @return mixed Datenstruktur für Formular - */ - function getEntry($id, &$result, $accounts){ - $details = model('App\Models\mBillDetails'); - $quelle = model('App\Models\mAccounts'); - $data = $this->orderBy('datum','desc')->find($id); - $result['datum'] = $data->datum;//date( "d.m.Y", strtotime($data->datum)); - $result['receiver'] = $data->receiver; - $result['billfile'] = $data->rechnung; - $result['source'] = $data->source_id; - $result['validate'] = $data->validate; - $result['id'] = $id; - $result = array_merge($result, $details->getEntriesForForm($id, $result, $accounts)); - $result['transfer'] = $result['transfer'] || ($data->type == 'transfer'); + } + + public function getToDos(){ + $result = $this->orderBy('datum','desc')->where('validate',true)->findAll(); return $result; - } + } - function getToDos(){ - $details = model('App\Models\mBillDetails'); - $quelle = model('App\Models\mAccounts'); - $result = $this->orderBy('datum','desc')->where('validate',true)->where('lkz',null)->findAll(); - $keys=array(); - foreach ($result as $value) { - $keys[] = intval($value->id); - } - $ergebnis = $details->getDetails($keys); - - foreach ($result as $value) { - $value->details = $ergebnis[$value->id]??null; - //$value->details = $details->getEntries($value->id); - $value->datum = date( "d.m.Y", strtotime( $value->datum )); - $value->quelle = $quelle->getDropDownEntry($value->source_id);; -// $value->amount = number_format( $value->amount , 2, ',', '.' ); - } - return $result; - } - function getAccountsBalance(){ - $sums = $this->db->query("SELECT * FROM get_account_balance4()"); - return $sums->getResult(); + public function getAccountsBalance(){ + $sums = $this->db->query("SELECT * FROM finanzen.getbalances('".auth()->user()->preFixDB."'::text,ARRAY[]::integer[])"); // mit datensyntax ARRAY[210,216] + // log_message('debug', $this->db->getLastQuery()); +// log_message('debug', print_r($sums->getResultArray(), true)); + return $sums->getResultArray(); } public function storeBillFile(int $billId, string $filename){ @@ -148,52 +144,49 @@ class mBills extends Model { $this->update($billId); } - public function saveBill($data) : int { - $details = model('App\Models\mBillDetails'); - $accounts = model('App\Models\mAccounts'); - $this->set('datum', date( "Y-m-d", strtotime($data['datum'] ) )); - - $this->set('source_id', $data['source']); - $total = floatval($data['total_in'])-floatval($data['total_out']); - if ( !isset($data['multi']) ) - $total = floatval($data['input'][0])-floatval($data['output'][0]); - if ( isset($data['rechnung']) ) - $this->set('rechnung', $data['rechnung']); - $this->set('amount', $total); - $this->set('validate', $data['validate']??false); - if ($data['transfer']==1){ - $this->set('receiver', $accounts->getDropDownEntry($data['category'][0])); - $this->set('type', 'transfer'); - } - else{ - $this->set('receiver', $data['receiver']); - $this->set('type', 'multiple'); - } - //$tabdata['type'] - if ( 0 == $data['id'] ){ - $this->insert(); - $billId = $this->getInsertID(); - //insert - } - else{ - $billId = $data['id']; - $this->update($data['id']); - } - $details->saveEntries($data, $billId); - return $billId; - } - /** * löscht gegebene Buchung inkl. Details * @param int id der Buchung * @return mixed Datenstruktur für Formular */ - function deleteEntry($id){ + public function deleteEntry(int $id) : bool { + $result = true; $details = model('App\Models\mBillDetails'); if ($id > 0){ - $details->where('bill_id',$id)->delete(); - $this->delete($id); + $result = $details->where('bill_id',$id)->delete(); + if ($result === true) + $result = $this->delete($id); } + return $result; } + + public function checkOrCreateTable(){ + log_message('debug',"Check or Create Bill Table"); + if ($this->db->tableExists('finanzen.'.$this->table)) return; + log_message('debug','Create Bills Table '.$this->db->lastQuery); + log_message('debug',"Create Bills Table ".$this->table); + $accounts = model('App\Models\mAccounts'); /* @var \App\Models\mAccounts $accounts */ + $forge = \Config\Database::forge(); + $fields = array( + 'id' => array( 'type' => 'serial'), + 'datum' => array( 'type' => 'date'), + 'receiver' => array( 'type' => 'character varying','constraint' => '120'), + 'source_id' => array( 'type' => 'integer' ), + 'amount' => array( 'type' => 'numeric','constraint' => '9,2' ), + 'validate' => array( 'type' => 'boolean' ), + 'rechnung' => array( 'type' => 'character varying','constraint' => '50','null'=>true), + 'deleted_at'=> array( 'type' => 'timestamp without time zone','null'=>true), + ); + $forge->addField( $fields ); + $forge->addPrimaryKey('id'); + $forge->addForeignKey('source_id', $accounts->getTable(), 'id','NO ACTION','NO ACTION','source_id_fk'); + // $this->db->query("CREATE TABLE $this->table (LIKE $this->defaultTableName INCLUDING DEFAULTS INCLUDING CONSTRAINTS) PARTITION BY RANGE (datum);"); + $forge->createTable($this->table,true,['PARTITION BY RANGE'=>'(datum)']); + log_message('debug','Create Bills Table '.$this->db->lastQuery); + // CREATE TABLE temperatures_y2025q1 PARTITION OF temperatures + // FOR VALUES FROM ('2025-01-01') TO ('2025-04-01'); + } + + }; ?> \ No newline at end of file diff --git a/app/Models/mScheduled.php b/app/Models/mScheduled.php index 76c6cc3..0127207 100644 --- a/app/Models/mScheduled.php +++ b/app/Models/mScheduled.php @@ -3,17 +3,26 @@ use CodeIgniter\Model; class mScheduled extends Model { - protected $table = 'budget_scheduled'; - protected $primaryKey = 'id'; + private $defaultTableName='_budget_scheduled'; + protected $primaryKey = 'id'; protected $useAutoIncrement = true; protected $returnType = 'array'; - protected $useSoftDeletes = false; + protected $useSoftDeletes = true; + protected $deletedField ="deleted_at"; + protected $allowedFields = ['creat_date','next_date','data']; protected $useTimestamps = false; protected $skipValidation = false; - public function calcNextDate($date, $num, $type) + + protected function initialize() { + $this->table = auth()->user()->preFixDB.$this->defaultTableName; + $this->checkOrCreateTable(); + } + + + public function calcNextDate(string $date, string $num, string $type) { $start = strtotime( $date ); $period = $num .' '. $type; @@ -24,68 +33,78 @@ class mScheduled extends Model { return date( "Y-m-d", $future ); } - public function getEntry($id){ - $accounts = model('App\Models\mAccounts'); + public function getEntry(int $id){ $data = $this->asObject()->find($id); $result = (array)json_decode($data->data); $result['id'] = $id; $result['datum'] = $data->next_date; //date( "d.m.Y", strtotime($data->next_date)); - for ($i=0;$i<10;$i++){ - $result['category_parent'][$i]= $accounts->getParent($result['category'][$i]); - } return $result; } public function getEntries(){ $quelle = model('App\Models\mAccounts'); $result = $this->asObject()->findAll(); + log_message('error', 'Get Entries Schedules: {msg}', ['msg'=>(string)$this->db->getLastQuery()]); foreach ($result as $value) { $details = json_decode($value->data); - for ($i=0;$i<10;$i++){ - if ((floatval($details->input[$i]) == 0.0) && (floatval($details->output[$i]) == 0.0)) + foreach ($details->input as $key => $line){ + if ((floatval($line) == 0.0) && (floatval($details->output[$key]) == 0.0)) continue; - $value->details[] = ['comment'=>$details->comment[$i], 'description'=>$quelle->getDropDownEntry($details->category[$i]), 'subamount'=>(floatval($details->input[$i])-floatval($details->output[$i]))]; + // $account_id = (!isset($details->account_id))?$details->category:$details->account_id; + $value->details[] = ['comment'=>$details->comment[$key], 'description'=>$quelle->getDropDownEntry($details->account_id[$key]), 'subamount'=>(floatval($details->input[$key])-floatval($details->output[$key]))]; } //$value->details = null; $value->datum = date( "d.m.Y", strtotime( $value->next_date )); - $value->source = $quelle->getDropDownEntry($details->source); + $source_id = (!isset($details->source_id))?$details->source:$details->source_id; + $value->source = $quelle->getDropDownEntry($source_id); $value->receiver = $details->receiver; - $value->amount = floatval($details->total_in)-floatval($details->total_out); -// $value->amount = number_format( $value->amount , 2, ',', '.' ); + // if (!isset($details->amount)){ + // $value->amount = floatval($details->total_in)-floatval($details->total_out); + // $value->amount = number_format( floatval($value->amount) , 2, ',', '.' ); + // } + // else + $value->amount = $details->amount; //number_format( floatval($details->amount) , 2, ',', '.' ); } return $result; + } - public function saveBill($data){ + public function saveIt(array $data){ $accounts = model('App\Models\mAccounts'); $data['validate'] = true; $data['scheduled'] = true; - $data['renummer'] = isset($data['renummer']); - if ($data['transfer']==1){ - $data['receiver'] = $accounts->getDropDownEntry($data['category'][0]); - $data['transfer'] = true; - } - else{ - $data['multi'] = isset($data['multiple']); - $data['transfer'] = false; - } unset($data['openamount']); unset($data['category_parent']); - $data['datum'] = date( "Y-m-d", strtotime($data['datum'] ) ); + unset($data['total_in']); + unset($data['total_out']); + +// $data['datum'] = date( "Y-m-d", strtotime($data['datum'] ) ); $this->set('next_date', $data['datum']); - - $data['total_in'] = 0; - $data['total_out'] = 0; - for ($i=0;$i<10;$i++){ - if ($data['category'][$i] > 0){ - $data['total_in'] += floatval($data['input'][$i]); - $data['total_out'] += floatval($data['output'][$i]); - } - } + $this->set('data',json_encode($data)); - + + log_message("debug","save Scheduled".json_encode($data)); if ( 0 == $data['id']) $this->insert(); else $this->update($data['id']); + } + + public function checkOrCreateTable(){ + log_message('debug',"Check or Create Scheduled Table"); + if ($this->db->tableExists($this->table)) return; + log_message('debug','Create Scheduled Table '.$this->db->lastQuery); + log_message('debug',"Create Scheduled Table ".$this->table); + $forge = \Config\Database::forge(); + $fields = array( + 'id' => array( 'type' => 'serial'), + 'creat_date' => array( 'type' => 'date','default'=>new \CodeIgniter\Database\RawSql('CURRENT_TIMESTAMP')), + 'next_date' => array( 'type' => 'date'), + 'data' => array( 'type' => 'text'), + 'deleted_at'=> array( 'type' => 'timestamp without time zone','null'=>true), + ); + $forge->addField( $fields ); + $forge->addPrimaryKey('id'); + $forge->createTable($this->table,true); + log_message('debug','Create Scheduled Table '.$this->db->lastQuery); } } \ No newline at end of file diff --git a/app/Views/accountsform.php b/app/Views/accountsform.php index 5339cba..9a85cd2 100644 --- a/app/Views/accountsform.php +++ b/app/Views/accountsform.php @@ -2,13 +2,13 @@
-
+ - +