redesign for mulituser

change validation concept (client and server)
using entities
This commit is contained in:
Markus
2026-10-06 08:55:35 +02:00
parent 14e050e4ba
commit 995e2ec111
41 changed files with 2402 additions and 1223 deletions

View File

@@ -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

View File

@@ -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');

View File

@@ -0,0 +1,44 @@
<?php namespace App\Controllers;
use CodeIgniter\Session\Session;
use App\Models\mAccounts;
use Config\App;
class Accounts extends BaseController {
private \App\Models\mAccounts $accounts;
public function __construct() {
$this->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]);
}
};

View File

@@ -1,71 +0,0 @@
<?php
namespace App\Controllers;
use App\Models\mBills;
use CodeIgniter\Session\Session;
class Ajax extends BaseController
{
protected $session;
public function __construct()
{
$this->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"]);
}
}
?>

90
app/Controllers/Bills.php Normal file
View File

@@ -0,0 +1,90 @@
<?php namespace App\Controllers;
use App\Models\mAccounts;
use function PHPUnit\Framework\logicalXor;
use App\Models\mBills;
use CodeIgniter\API\ResponseTrait;
include_once (dirname(__FILE__) . "/FormHandler.php");
class Bills extends FormHandler {
use ResponseTrait;
public function __construct() {
}
public function edit(int $id, bool $transfer=false){
if ($id == 0){ // erstellen eines leeren Formular
log_message('debug','Bills::edit: new bill');
$eBooking = new \App\Entities\eBooking();
$eBooking->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());
}
}

View File

@@ -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 = <<<EOD
SELECT
SUM(subamount)*(-1) AS Amount,
description,account_id as id
FROM
finanzen.budget_billdetails, finanzen.budget_bills,finanzen.budget_accounts
$billDetTable, $billTable,$accTable
WHERE
datum >= '$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 = <<<EOD
SELECT * FROM (
SELECT id, description,sum(amount)as summe FROM (SELECT id,description from finanzen.budget_accounts WHERE parent_id=0 AND type='output' AND LKZ is null) as w,
SELECT id, description,sum(amount)as summe FROM (SELECT id,description from $accTable WHERE parent_id=0 AND account=false AND deleted_at is null) as w,
LATERAL
(SELECT ABS(SUM(subamount)) as amount
FROM finanzen.budget_billdetails, finanzen.budget_bills
FROM $billDetTable, $billTable
WHERE datum >= '$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;

View File

@@ -0,0 +1,195 @@
<?php namespace App\Controllers;
use App\Models\mAccounts;
use function PHPUnit\Framework\logicalXor;
use App\Models\mBills;
class FormHandler extends BaseController {
public function editForm(){
if ($this->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;
}
}

View File

@@ -1,352 +1,28 @@
<?php
namespace App\Controllers;
use App\Models\mBills;
use App\Models\mAccounts;
use App\Models\mBills;
use CodeIgniter\Session\Session;
use CodeIgniter\Files\File;
use CodeIgniter\I18n\Time;
class Home extends BaseController
{
private $accounts;
class Home extends BaseController {
/**
* @var Session
*/
protected $session;
public function __construct()
{
public function __construct() {
$this->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 <br/>";
//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');
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace App\Controllers;
use App\Models\mScheduled;
use CodeIgniter\Session\Session;
use CodeIgniter\API\ResponseTrait;
include_once (dirname(__FILE__) . "/FormHandler.php");
class Scheduled extends FormHandler {
use ResponseTrait;
/**
* @var Session
*/
protected $session;
public function __construct() {
$this->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 <br/>";
$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());
}
}

194
app/Entities/eBooking.php Normal file
View File

@@ -0,0 +1,194 @@
<?php
namespace App\Entities;
use App\Models\mBills;
use App\Models\mBillDetails;
use App\Models\mScheduled;
use CodeIgniter\Entity\Entity;
use CodeIgniter\I18n\Time;
class eBooking extends Entity{
protected $file = null; /* @var \CodeIgniter\HTTP\Files\UploadedFile|null $file */
protected $attributes =[
'id' => 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;
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Entities;
use CodeIgniter\Entity\Entity;
use App\Models\mAccounts;
class eBookingLine extends Entity{
protected $attributes =[
'id' => 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]);
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* 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.',
];

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Models\Cast;
//namespace CodeIgniter\DataCaster\Cast;
use CodeIgniter\Database\BaseConnection;
use CodeIgniter\Exceptions\InvalidArgumentException;
use CodeIgniter\DataCaster\Cast\BaseCast;
use CodeIgniter\I18n\Time;
// The class must inherit the CodeIgniter\DataCaster\Cast\BaseCast class
class CastMyDate extends BaseCast
{
public static function get( mixed $value, array $params = [], ?object $helper = null,): string {
if (! is_string($value)) {
self::invalidTypeValueError($value);
}
if (! $helper instanceof BaseConnection) {
$message = 'The parameter $helper must be BaseConnection.';
throw new InvalidArgumentException($message);
}
$format = $helper->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);
}
}

View File

@@ -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');
}
};
?>

View File

@@ -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) {
protected array $casts = [
'subamount' => 'float',
'account_id' => 'int',
'bill_id' => 'int',
];
// // $value->datum = date( "d.m.Y", strtotime( $value->datum ));
// // $value->amount = number_format( $value->amount , 2, ',', '.' );
// }
// return $result;
// }
protected $validationRules = [
'bill_id' => 'required|numeric',
'account_id' => 'required|numeric',
];
public function getDetails($ids){
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<int> $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);
}
};
?>

View File

@@ -1,33 +1,58 @@
<?php namespace App\Models;
use App\Models\Cast\CastMyDate;
use CodeIgniter\Model;
use App\Models\Mbookingdetails;
use App\Models\Mcategories;
class mBills extends Model {
protected $table = 'budget_bills';
private $defaultTableName='_budget_bills';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'object';
protected $returnType = \App\Entities\eBooking::class;
protected $useSoftDeletes = true;
protected $deletedField ="deleted_at";
protected $useSoftDeletes = false;
//TODO timestamps aktivieren
// protected $createdField = 'created_at';
// protected $updatedField = 'updated_at';
// protected $deletedField = 'deleted_at';
protected $allowedFields = ['renummer','datum','receiver','source_id','amount','type','comment','validate','lkz','rechnung'];
protected $allowedFields = ['datum','receiver','source_id','amount','validate','deleted_at','rechnung'];
protected $useTimestamps = false;
// Bind the type to the handler
protected array $castHandlers = [
'myDate' => 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');
}
};
?>

View File

@@ -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);
}
}

View File

@@ -2,13 +2,13 @@
<div class="card text-bg-dark">
<div class="card-content">
<div class="card-body">
<form class="form form-vertical" id="editaccount" method="post" action="<?= base_url() ?>/editAccount">
<form class="form form-vertical" id="editaccount" method="post" action="<?= base_url() ?>/viewAccounts">
<input type="hidden" value="<?= $id??0 ?>" name="id" id="id">
<?php if (! empty($validation)) : ?>
<?php if (! empty($errors)) : ?>
<div class='alert alert-danger mt-2'>
<div class="errors" role="alert">
<ul>
<?php foreach ($validation as $error) : ?>
<?php foreach ($errors as $error) : ?>
<li><?= esc($error) ?></li>
<?php endforeach ?>
</ul>
@@ -26,33 +26,36 @@
<div class="col-12">
<div class="form-group">
<label for="parent">Überkonto:</label>
<?= form_dropdown('parent', $ddlist, $parent??0,'id="parent" class="form-select"'); ?>
<select name="parent" class="form-select" required id="parent" >
<option value="0">----</option>
<?php foreach ($ddlist as $key => $value): ?>
<option value="<?= $key ?>" <?= ($key == ($parent??0))?'selected':'' ?>><?= $value ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="col-12">
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="typeAccount" value="account" name="type" <?= set_radio('type', 'account') ?>>
<label class="form-check-label" for="inlineCheckbox1">Konto</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="typeInput" value="input" name="type" <?= set_radio('type', 'input') ?> >
<label class="form-check-label" for="inlineCheckbox2">Einnahmen</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="typeOutput" value="output" name="type" <?= set_radio('type', 'output', true) ?> >
<label class="form-check-label" for="inlineCheckbox3">Ausgaben</label>
<div class="form-check form-switch">
<label class="form-check-label" for="account">Typ Konto</label>
<input class="form-check-input" type="checkbox" id="account" name="account" <?= set_checkbox('account', 'on', $account??false) ?> >
</div>
</div>
<div class="col-12">
<div class="form-check form-switch">
<label class="form-check-label" for="bookable">Buchbarkeit</label>
<input class="form-check-input" type="checkbox" id="bookable" value="bookable" name="bookable" <?= set_checkbox('bookable', 'on', (($bookable??"f")=="t")?true:false) ?> >
<input class="form-check-input" type="checkbox" id="bookable" value="bookable" name="bookable" <?= set_checkbox('bookable', 'on', $bookable??false) ?> >
</div>
</div>
<div class="col-12">
<div class="form-check form-switch">
<label class="form-check-label" for="lkz">lkz</label>
<input class="form-check-input" type="checkbox" id="lkz" value="1" name="lkz" <?= set_checkbox('lkz', '1', $lkz??false) ?>>
<label class="form-check-label" for="monitor">Monitor</label>
<input class="form-check-input" type="checkbox" id="monitor" name="monitor" <?= set_checkbox('monitor', 'on', $monitor??false) ?> >
</div>
</div>
<div class="col-12">
<div class="form-check form-switch">
<label class="form-check-label" for="deleted_at">Gelöscht</label>
<input class="form-check-input" type="checkbox" id="deleted_at" value="1" name="deleted_at" <?= set_checkbox('deleted_at', '1', $deleted_at??false) ?>>
</div>
</div>
<div class="col-12">

View File

@@ -3,31 +3,49 @@
<?= $this->section('css'); ?>
<link rel="stylesheet" href="/css/mawim.css">
<link rel="stylesheet" href="/assets/jquery-ui-1.13.2.custom/jquery-ui.css">
<?= $this->endSection(); ?>
<?= $this->section('menu'); ?>
<?= $this->include('sidebar') ?>
<?= $this->endSection(); ?>
<?= $this->section('content'); ?>
<?php if (!function_exists('printBool')) {
function printBool(bool $var){
return ($var)?"true":"false";
}
}
?>
<?php function printsub($id, $all){
<?php if (!function_exists('printsub')) { function printsub(int $id, $all){
$liste = '';
$ret='';
foreach($all as $ele){
$class ="";
$vis ="d-none";
if ($ele->parent_id == $id){
if ($ele->lkz) $class = "text-bg-danger";
else if ($ele->bookable == "f") $class = "text-bg-warning";
$toolt ="bookable: $ele->bookable; lkz: $ele->lkz; Id: $ele->id; PID:$ele->parent_id";
$liste .= "<li><span data-bs-toggle='tooltip' data-bs-html=true title='$toolt'";
if ($ele->deleted_at) {
$class = "text-bg-danger";
$vis="d-none";
}
else {
$vis="";
if (!$ele->bookable)
$class = "text-bg-warning";
}
$toolt = ($ele->bookable)?"":"not ";
$toolt .= "bookable; ";
$toolt .= ($ele->deleted_at)?"gelöscht; ":"";
$toolt .="Id: $ele->id; PID:$ele->parent_id";
$liste .= "<li class=\"$vis\"><i class=\"fa-solid\"></i> <span data-bs-toggle='tooltip' data-bs-html=true title='$toolt'";
$liste .= "data-id=\"$ele->id\"";
$liste .= "data-pid=\"$ele->parent_id\"";
$liste .= "data-bookable=\"$ele->bookable\"";
$liste .= "data-lkz=\"$ele->lkz\"";
$liste .= "data-type=\"$ele->type\"";
$liste .= "data-bookable=\"".printBool($ele->bookable)."\"";
$liste .= "data-account=\"".printBool($ele->account)."\"";
$liste .= "data-monitor=\"".printBool($ele->monitor)."\"";
$liste .= "data-deleted=\"$ele->deleted_at\"";
$liste .= "data-description=\"$ele->description\"";
$liste .= "class='$class'>$ele->description</span></li>";
$liste .= "class='$class'>$ele->description</span>";
$liste .= printsub($ele->id, $all);
$liste .= "</li>";
}
}
if ($liste !=''){
@@ -36,33 +54,53 @@
$ret .= "</ul>";
}
return $ret;
} ?>
}} ?>
<div class="page-heading">
<h3> Kategorien bearbeiten</h3>
<?php if ($saveDB??false) : ?>
<div class='alert <?= $saveDB["mode"] ?> mt-2'>
<?= $saveDB["message"] ?>
</div>
<?php endif; ?>
</div>
<div class="row">
<div class="col-md-3 col-12" id="tree">
<div class="card text-bg-dark">
<div class="card-content">
<div class="card-body">
<div><a class="btn btn-info multi" onclick="newCat();" role="button">Neue Kategorie</a></div>
<div class="d-flex justify-content-between">
<div class="col-sm-12 col-md-6">
<a class="btn btn-info multi" onclick="newCat();" role="button"> + Neue Kategorie</a>
</div>
<div class="col-sm-12 col-md-6 form-check form-check-reverse form-switch justify-content-end">
<input class="form-check-input" type="checkbox" id="viewdeleted" value="1" name="viewdeleted">
<label class="form-check-label" for="viewdeleted">Gelöschte</label>
</div>
</div>
<div class="tree">
<ul>
<?php foreach($parents as $parent): ?>
<?php $class='';
if ($parent->lkz) $class = "text-bg-danger";
else if ($parent->bookable == "f") $class = "text-bg-warning"; ?>
<li> <span data-bs-toggle='tooltip'
<?php $class=''; $vis ="d-none";
if ($parent->deleted_at) {
$class = "text-bg-danger";
$vis = "d-none";
} else {
$vis = "";
if (!$parent->bookable) $class = "text-bg-warning";
}?>
<li class="<?=$vis?>"><i class="fa-solid"></i> <span data-bs-toggle='tooltip'
data-bs-html=true
data-id="<?=$parent->id?>"
data-pid="0"
data-bookable="<?=$parent->bookable?>"
data-lkz=" <?= $parent->lkz?>"
data-type="<?= $parent->type?>"
data-bookable=<?=printBool($parent->bookable)?>
data-monitor=<?=printBool($parent->monitor)?>
data-account=<?=printBool($parent->account)?>
data-deleted="<?= $parent->deleted_at?>"
data-description="<?= $parent->description?>";
title="bookable: <?=$parent->bookable?>; lkz: <?= $parent->lkz?>; Id: <?=$parent->id?>;" class="<?=$class?>"><?=$parent->description?></span>
title="<?=($parent->bookable)?"":"not "?>bookable; <?= ($parent->deleted_at)?"gelöscht; ":""?>Id: <?=$parent->id?>;"
class="<?=$class?>"><?=$parent->description?></span>
<?= printsub($parent->id, $all); ?>
<li>
<?php endforeach; ?>
</ul>
</div>
@@ -70,13 +108,11 @@
</div>
</div>
</div>
<div class="col-md-3 col-12 d-none" id="editor">
<div class="col-md-3 col-12 <?= (empty($errors))?"d-none" :"" ?>" id="editor">
<?= $this->include('accountsform') ?>
</div>
</section>
<?= $this->endSection(); ?>
<?= $this->section('scripts'); ?>
<script src="/assets/jquery-ui-1.13.2.custom/jquery-ui.js"></script>
<script></script>
<script src="/js/accounts.js"></script>
<?= $this->endSection(); ?>

View File

@@ -1,7 +1,6 @@
<?= $this->extend('layout'); ?>
<?= $this->section('css'); ?>
<link rel="stylesheet" href="/assets/jquery-ui-1.13.2.custom/jquery-ui.css">
<link rel="stylesheet" href="/css/mawim.css">
<?= $this->endSection(); ?>
<?= $this->section('menu'); ?>
@@ -13,71 +12,40 @@
<div class="row">
<div class="col-xxl-8 col-12">
<div class="text-white-50"><h2>Verlauf</h2></div>
<form class="form row row-cols align-items-bottom" id="getData" method="post" action="<?= base_url() ?>/Ajax/getTabelData">
<form class="form row row-cols align-items-bottom" id="getData" method="post" action="/getActivitiesData">
<div class="row">
<div class="col-lg-3 col-12">
<input type="text" class="form-control" id="datepickers" data-date-format="mm.dd.yyyy" name="datum_start" placeholder="Datum Start" value="<?= $datum_start??$start ?>"/>
<input type="date" class="form-control" name="datum_start" placeholder="Datum Start" value="<?= $datum_start??$start ?>"/>
</div>
<div class="col-lg-3 col-12">
<input type="text" class="form-control" id="datepickere" data-date-format="mm.dd.yyyy" name="datum_ende" placeholder="Datum Ende" value="<?= $datum_ende??$ende ?>"/>
<input type="date" class="form-control" name="datum_ende" placeholder="Datum Ende" value="<?= $datum_ende??$ende ?>"/>
</div>
<div class="col-lg-3 col-12">
<div class="form-group">
<?= form_dropdown('source', $sourcelist, $source,'id="source" class="form-select"'); ?>
<select name="source" class="form-select">
<option value="0">----</option>
<?php foreach ($sourcelist as $key => $value): ?>
<option value="<?= $key ?>" <?= ($key == $source)?'selected':'' ?>><?= $value ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="col-lg-3 col-12">
<a class="btn btn-primary" onclick="datatable.ajax.reload();" role="button">Aktualisieren</a>
<a class="btn btn-primary" onclick="loadActivityData();" role="button">Aktualisieren</a>
</div>
</div>
</form>
<table class="table table-sm table-striped" id="bookings" cellspacing="0"></table>
<table id="activities" cellspacing="0"></table>
</div>
<div class="col-xxl-4 col-12 d-none d-xxl-block">
<div class="text-white-50"><h2>Aktuelle Kontostände</h2></div>
<table class="table table-striped table-sm" id="accounts" cellspacing="0"></table>
<table id="accounts" cellspacing="0"></table>
</div>
</div>
</div>
<?= $this->endSection(); ?>
<?= $this->section('scripts'); ?>
<script src="/assets/jquery-ui-1.13.2.custom/jquery-ui.js"></script>
<script>
$.datepicker.regional['de'] = {clearText: 'löschen', clearStatus: 'aktuelles Datum löschen',
closeText: 'schließen', closeStatus: 'ohne Änderungen schließen',
prevText: '<zurück', prevStatus: 'letzten Monat zeigen',
nextText: 'Vor>', nextStatus: 'nächsten Monat zeigen',
currentText: 'heute', currentStatus: '',
monthNames: ['Januar','Februar','März','April','Mai','Juni',
'Juli','August','September','Oktober','November','Dezember'],
monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun',
'Jul','Aug','Sep','Okt','Nov','Dez'],
monthStatus: 'anderen Monat anzeigen', yearStatus: 'anderes Jahr anzeigen',
weekHeader: 'Wo', weekStatus: 'Woche des Monats',
dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'],
dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'],
dayStatus: 'Setze DD als ersten Wochentag', dateStatus: 'Wähle D, M d',
dateFormat: 'dd.mm.yy', firstDay: 1,
initStatus: 'Wähle ein Datum', isRTL: false};
$.datepicker.setDefaults($.datepicker.regional['de']);
$('#datepickers').datepicker(
{
format: "dd.mm.yyyy",
language: 'de-DE',
weekStart: 1,
autoclose: true,
todayHighlight: false});
$('#datepickere').datepicker(
{
format: "dd.mm.yyyy",
language: 'de-DE',
weekStart: 1,
autoclose: true,
todayHighlight: false});
</script>
<script src="/js/tables.js"></script>
<script src="/js/tab_accounts.js"></script>
<script src="/js/activities.js"></script>
<script src="/js/tab_activities.js"></script>
<?= $this->endSection(); ?>

View File

@@ -0,0 +1,56 @@
<?php
if (!$template) {
$parentList = $booking['cat_parent'][$i];
$subList = $booking['cat_sub'][$i];
$account_id = $booking['account_id'][$i];
$parent_id = $booking['parent_id'][$i];
$comment = $booking['comment'][$i];
$input = $booking['input'][$i];
$output = $booking['output'][$i];
$sid = $booking['sid'][$i];
} else {
$i = 0;
$account_id = 0;
$parent_id = 0;
$parentList = $booking['category']['parent'];
$subList = $booking['category']['subs']->{$parent_id};
$comment = '';
$input = '';
$output = '';
$sid = 0;
}
?>
<div <?=(($template)?'id="template"':'')?> class="row <?=(($template)?'d-none':'')?>">
<input type="hidden" value="<?= $sid ?>" name="sid[]">
<div class="col-lg-3 col-6 col-md-6 <?=(($i>0)?'multi':'')?> order-sm-2 px-0">
<select name="parent_id[]" class="form-select parent" <?=(($i==0 && !$template)?'required':'')?>>
<option value="">----</option>
<?php foreach ($parentList as $key => $value): ?>
<option value="<?= $key ?>" <?= ($key == $parent_id)?'selected':'' ?>><?= $value ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-lg-3 col-6 col-md-6 <?=(($i>0)?'multi':'')?> order-sm-2 px-0" <?=(($i==0 && !$template)?'required':'')?>>
<select name="account_id[]" class="form-select sub">
<?php foreach ($subList as $key => $value): ?>
<option value="<?= $key ?>" <?= ($key == $account_id && $account_id>0)?'selected':'' ?>><?= $value ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="w-100 d-md-none"></div>
<div class="col-lg-2 col-6 <?=(($i>0)?'multi':'')?> <?= ($booking['transfer']?' d-none':'') ?> px-0 order-sm-2">
<input type="text" name="input[]" class="form-control dt-subamount input" placeholder="Einnahme" value="<?= $input ?>"
pattern="[0-9\*\(\)\-\+\/,.]*"
oninput="if (!this.checkValidity()) this.value = this.value.slice(0, -1)">
</div>
<div class="<?= ($booking['transfer']?'col-lg-4 col-12':'col-lg-2 col-6') ?> <?=(($i>0)?'multi':'')?> px-0 order-sm-2">
<input type="text" name="output[]" placeholder="Ausgabe" value="<?= $output ?>"
pattern="[0-9\*\(\)\-\+\/,.]*"
oninput="if (!this.checkValidity()) this.value = this.value.slice(0, -1)" class="form-control dt-subamount output text-danger" >
</div>
<div class="col-lg-2 col-12 col-md-12 <?=(($i>0)?'multi':'')?> order-sm-1 px-0 mb-3 mb-md-0">
<input type="text" name="comment[]" class="form-control" placeholder="Kommentar" value="<?= $comment ?>">
</div>
</div>

View File

@@ -0,0 +1,19 @@
<div class="col-12 scheduled <?=($booking['scheduled']?'':'d-none')?>">
<div class="form-group">
<label for="scheduledNum">Scheduled:</label>
<div class="input-group">
<select name="scheduledNum" id="scheduledNum" class="form-select" required>
<option value="">----</option>
<?php foreach ($booking['schedule_lists']['NumList'] as $key => $value): ?>
<option value="<?= $key ?>" <?= ($key == $booking['scheduledNum'])?'selected':'' ?>><?= $value ?></option>
<?php endforeach; ?>
</select>
<select name="scheduledType" id="scheduledType" class="form-select" required>
<option value="">----</option>
<?php foreach ($booking['schedule_lists']['TypeList'] as $key => $value): ?>
<option value="<?= $key ?>" <?= ($key == $booking['scheduledType'])?'selected':'' ?>><?= $value ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>

View File

@@ -8,18 +8,14 @@
<meta name="description" content="Adminpage for Finanzen" />
<meta name="author" content="Wimpissinger Markus" />
<?= $this->renderSection('css_first') ?>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;600;700;800&display=swap" rel="stylesheet">
<link href="/assets/bootstrap-5.3.0-alpha1-dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="/assets/fontawesome6.4.0/css/fontawesome.css" rel="stylesheet">
<link href="/assets/fontawesome6.4.0/css/solid.css" rel="stylesheet">
<link href="/assets/fontawesome6.4.0/css/brands.css" rel="stylesheet">
<link href="/assets/fontawesome6.4.0/css/regular.css" rel="stylesheet">
<!-- <link rel="stylesheet" href="/assets/vendors/perfect-scrollbar/perfect-scrollbar.css"> -->
<link href="/assets/bootstrap/css/bootstrap.css" rel="stylesheet" />
<link href="/assets/font-awesome/css/fontawesome.css" rel="stylesheet">
<link href="/assets/font-awesome/css/solid.css" rel="stylesheet">
<link href="/assets/font-awesome/css/brands.css" rel="stylesheet">
<link href="/assets/font-awesome/css/regular.css" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/app.css">
<link href="/assets/DataTables/datatables.min.css" rel="stylesheet"/>
<!-- <script src="https://cdn.jsdelivr.net/npm/simple-datatables@latest" type="text/javascript"></script> -->
<?= $this->renderSection('css') ?>
<!-- <link rel="shortcut icon" href="/assets/images/favicon.svg" type="image/x-icon"> -->
</head>
<body>
@@ -34,20 +30,41 @@
</a>
</header>
<?= $this->renderSection('content') ?>
<div class="modal" id="mymodal" tabindex="-1">
<div class="modal fade" id="mymodal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header d-none">
<h1 class="modal-title fs-5" id="modalTitle">Modal Title</h1>
</div>
<div class="modal-body">
<h5 class="modal-title"></h5>
<p>Wirklich löschen?</p>
<p>Modal body</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbruch</button>
<button type="button" id="modal_ok" class="btn btn-primary">Löschen</button>
<div class ="mx-auto">
<button type="button" id="modal_ok" data-bs-dismiss="modal" class="btn btn-primary">Ok</button>
</div>
</div>
</div>
</div>
</div>
<div class="modal" id="mymodalDelete" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title fs-5" id="modalDelTitle">Wirklich löschen?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body d-none">
<p class="modal-text">Wirklich löschen?</p>
</div>
<div class="modal-footer">
<div class ="mx-auto">
<button type="button" id="modal_delete" class="btn btn-primary" data-bs-target="#mymodal" data-bs-toggle="modal">Löschen</button>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="mySnycModal" tabindex="-1" aria-labelledby="Snc Result Window" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
@@ -82,16 +99,14 @@
</div>
</div>
</div>
<!-- <script src="/assets/vendors/perfect-scrollbar/perfect-scrollbar.min.js"></script>
<script src="/assets/js/bootstrap.bundle.min.js"></script> -->
<script src="/assets/jquery-3.6.4.min.js" type="text/javascript"></script>
<script src="/assets/bootstrap-5.3.0-alpha1-dist/js/bootstrap.bundle.js" type="text/javascript"></script>
<script defer src="/assets/fontawesome6.4.0/js/solid.js"></script>
<script defer src="/assets/fontawesome6.4.0/js/brands.js"></script>
<script defer src="/assets/fontawesome6.4.0/js/regular.js"></script>
<script src="/assets/DataTables/datatables.min.js"></script>
<!-- <script src="/assets/vendors/perfect-scrollbar/perfect-scrollbar.min.js"></script> -->
<script src="/assets/bootstrap/js/bootstrap.bundle.js" type="text/javascript"></script>
<script src="/assets/fiduswriter/simple-datatables.js" type="text/javascript"></script>
<!-- <script defer src="/assets/fontawesome/js/solid.js"></script>
<script defer src="/assets/fontawesome/js/brands.js"></script>
<script defer src="/assets/fontawesome/js/regular.js"></script> -->
<script src="/js/menue.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/main.js"></script>
<?= $this->renderSection('scripts') ?>
</body>

View File

@@ -14,10 +14,10 @@
<div class="col-md-8 col-12">
<div class="card">
<div class="card-header pb-0">
<?php if ($id) : ?>
<h4 class="card-title"><?= ($transfer?'Transfer bearbeiten':($isScheduled?'Terminbuchung bearbeiten':'Rechnung bearbeiten'))?></h4>
<?php if ($booking['id']) : ?>
<h4 class="card-title"><?= ($booking['transfer']?'Transfer '.$booking['id'].' bearbeiten':($booking['scheduled']?'Terminbuchung '.$booking['id'].' bearbeiten':'Rechnung '.$booking['id'].' bearbeiten'))?></h4>
<?php else: ?>
<h4 class="card-title"><?= ($transfer?'Neuer Transfer':($isScheduled?'Neue Terminbuchung':'Neue Rechnung'))?></h4>
<h4 class="card-title"><?= ($booking['transfer']?'Neuer Transfer':($booking['scheduled']?'Neue Terminbuchung':'Neue Rechnung'))?></h4>
<?php endif; ?>
<?php if (! empty($validation)) : ?>
@@ -34,27 +34,25 @@
</div>
<div class="card-content">
<div class="card-body">
<?= form_open_multipart('newBill','class="form form-vertical" onsubmit="logData(event)" id="newbill"') ?>
<?= view("bill_bookingline",['booking'=>$booking, 'template'=>true]); ?>
<form id="newbill" action="/edit" class="form form-vertical needs-validation" method="post" enctype="multipart/form-data" onsubmit="checkForm(event,this)" novalidate>
<!-- <form class="form form-vertical" id="newbill" method="post" enctype="multipart/form-data" onsubmit="logData(event)" action="<?= base_url() ?>newBill"> -->
<input type="hidden" value="<?= $id ?>" name="id">
<input type="hidden" value="<?= $transfer?'1':'0' ?>" name="transfer">
<input type="hidden" value="<?= $booking['id'] ?>" name="id">
<input type="hidden" value="<?= $booking['amount']??0.0 ?>" name="amount">
<input type="hidden" value="<?= $booking['transfer']?'1':'0' ?>" name="transfer">
<input type="hidden" value="<?= ($booking['scheduled']??false)?'1':'0' ?>" name="scheduled">
<div class="form-body">
<div class="row">
<div class="col-12 <?= ($transfer?' d-none':'') ?>">
<div class="form-check form-switch">
<label class="form-check-label" for="multi">Multi Rechnung</label>
<input class="form-check-input" type="checkbox" id="multi" value="on" name="multi" <?= set_checkbox('multi', 'on', $multi??false) ?>>
</div>
</div>
<div class="col-12 <?= ($transfer?' d-none':($isScheduled?' d-none':'')) ?>">
<div class="col-12 <?= ($booking['transfer']?' d-none':(($booking['scheduled']??false)?' d-none':'')) ?>">
<label for="billfile">Rechnung:</label>
<input type="file" id="billfile" name="billfile" class="form-control <?= (!isset($billfile))?'':'d-none' ?>" capture="environment" accept="image/*, application/pdf">
<?php if (isset($billfile)): ?>
<a href="/viewBill/<?= $billfile ?>" id="billfileRef"><?= $billfile ?></a>
<input type="file" id="billfile" name="billfile" class="form-control <?= (!isset($booking['rechnung']))?'':'d-none' ?>" capture="environment" accept="image/*, application/pdf">
<?php if (isset($booking['rechnung'])): ?>
<a href="/viewInvoice/<?= $booking['rechnung'] ?>" id="billfileRef" target="_blank"> <?= $booking['rechnung'] ?></a>
<a class="" onclick="removeBillFile(this);"><i class="fa fa-trash"></i></a>
<?php endif; ?>
</div>
<div class="col-12 <?= ($transfer?' d-none':'d-none') ?>">
<div class="col-12 <?= ($booking['transfer']?' d-none':'d-none') ?>">
<div class='form-check'>
<div class="checkbox">
<label for="renummer">Re-Nr.</label>
@@ -64,76 +62,64 @@
</div>
<div class="col-12">
<label for="datum">Datum</label>
<input type="date" class="form-control" id="datum" name="datum" value="<?= $datum ?>" onclick="openBox(this);"/>
<input type="date" class="form-control" id="datum" name="datum" value="<?= $booking['datum'] ?>" onclick="openBox(this);"/>
</div>
</div>
<div class="col-12 <?= ($transfer?' d-none':'') ?>">
<div class="col-12 <?= ($booking['transfer']?' d-none':'') ?>">
<div class="form-group">
<label for="receiver">Empfänger</label>
<input type="text" class="form-control" id="receiver" name="receiver" value="<?= $receiver ?>">
<input type="text" class="form-control" id="receiver" name="receiver" <?=(($booking['transfer'])?'':'required')?> value="<?= $booking['receiver'] ?>">
</div>
</div>
<div class="col-12">
<div class="form-group">
<label for="source">Konto:</label>
<?= form_dropdown('source', $sourcelist, $source,'id="source" class="form-select"'); ?>
<label for="sourceID">Konto:</label>
<select name="source_id" class="form-select" required id="sourceID" >
<option value="">----</option>
<?php foreach ($booking['sourcelist'] as $key => $value): ?>
<option value="<?= $key ?>" <?= ($key == $booking['source_id'])?'selected':'' ?>><?= $value ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="col-12 multi">
<div class="input-group">
<input type="text" class="form-control dt-amount" id="total_in" name="total_in" placeholder="Einnahme" maxlength=120 value="<?= $total_in ?>">
<input type="text" class="form-control dt-amount text-danger" id="total_out" name="total_out" placeholder="Ausgabe" maxlength=120 value="<?= $total_out ?>">
</div>
</div>
<div class="col-12 scheduled <?=($isScheduled?'':'d-none')?>">
<div class="form-group">
<label for="scheduledNum">Scheduled:</label>
<div class="input-group">
<input type="hidden" value="<?= $isScheduled?'1':'0' ?>" name="scheduled">
<?= form_dropdown("scheduledNum", $scheduled['NumList'], $scheduledNum,$scheduled['style']); ?>
<?= form_dropdown("scheduledType",$scheduled['TypeList'], $scheduledType,$scheduled['style']); ?>
</div>
<div class="input-group has-validation">
<input type="text" id="total_in" name="total_in" value="<?= $booking['total_in'] ?>"
class="form-control dt-amount input"
pattern="[0-9\*\(\)\-\+\/,.]*"
oninput="if (!this.checkValidity()) this.value = this.value.slice(0, -1)"
placeholder="Einnahme" maxlength=120 ">
<input type="text" id="total_out" name="total_out" value="<?= $booking['total_out'] ?>"
class="form-control dt-amount output text-danger"
pattern="[0-9\*\(\)\-\+\/,.]*"
oninput="if (!this.checkValidity()) this.value = this.value.slice(0, -1)"
placeholder="Ausgabe" maxlength=120>
</div>
</div>
<?php if ($booking['scheduled']??false)
echo view("bill_scheduled",['booking'=>$booking]);
?>
<div class="col-12 d-none multi">
<div class="form-group">
<label for="openamount">Offener Betrag:</label>
<input type="text" class="form-control dt-amount" readonly="readonly" id="openamount" name="openamount">
<div class="invalid-feedback"></div>
</div>
</div>
<div class="col-12" id="linecont">
<?php for ($i=0;$i<10;$i++): ?>
<div class="row <?= (($i<3 || $input[$i]>0 || $output[$i]>0)?'':' d-none')?>">
<input type="hidden" value="<?= $sid[$i] ?>" name="sid[<?=$i?>]">
<div class="col-lg-3 col-6 col-md-6 <?=(($i>0)?'multi':'')?> order-sm-2 px-0">
<?= form_dropdown("category_parent[$i]", $subsparent[$i], $category_parent[$i], 'class="form-select parent"'); ?>
</div>
<div class="col-lg-3 col-6 col-md-6 <?=(($i>0)?'multi':'')?> order-sm-2 px-0 sub">
<?= form_dropdown("category[$i]", $subscategory[$i], $category[$i], 'class="form-select"'); ?>
</div>
<div class="w-100 d-md-none"></div>
<div class="col-lg-2 col-6 <?=(($i>0)?'multi':'')?> <?= ($transfer?' d-none':'') ?> px-0 order-sm-2">
<input type="text" name="input[<?=$i?>]" class="form-control dt-subamount" placeholder="Einnahme" value="<?= $input[$i] ?>">
</div>
<div class="<?= ($transfer?'col-lg-4 col-12':'col-lg-2 col-6') ?> <?=(($i>0)?'multi':'')?> px-0 order-sm-2">
<input type="text" name="output[<?=$i?>]" class="form-control dt-subamount text-danger" placeholder="Ausgabe" value="<?= $output[$i] ?>">
</div>
<div class="col-lg-2 col-12 col-md-12 <?=(($i>0)?'multi':'')?> order-sm-1 px-0 mb-3 mb-md-0">
<input type="text" name="comment[<?=$i?>]" class="form-control" placeholder="Kommentar" value="<?= $comment[$i] ?>">
</div>
</div>
<?php endfor; ?>
<?php $i = 0; foreach ($booking['sid'] as $_sid) :
echo view("bill_bookingline",['booking'=>$booking, 'i'=>$i,'template'=>false]);
?>
<?php $i++; endforeach; ?>
</div>
<div class="col-12 d-flex" role="group">
<a class="btn btn-info multi" onclick="addLine();" role="button">+ Zeile</a>
<input type="checkbox" id="validate" name="validate" value="1" class='btn-check' autocomplete="off" <?= set_checkbox('validate', '1', (($validate??"f")=="t")?true:false) ?>>
<a id="multiadd" class="btn btn-info <?= ($booking['transfer']?' d-none':'') ?>" onclick="addLine();" role="button">+ Zeile</a>
<input type="checkbox" id="validate" name="validate" value="1" class='btn-check' autocomplete="off" <?= set_checkbox('validate', '1', ($booking['validate'])?true:false) ?>>
<label class="btn btn-outline-danger" for="validate">Entwurf</label>
<?php if ($id) : ?>
<a class="btn btn-danger" data-bs-toggle="modal" data-bs-id="<?= $id ?>" data-bs-target="#mymodal"><i class="fa fa-trash"></i></a>
<?php if ($booking['id']) : ?>
<a class="btn btn-danger" data-bs-toggle="modal" data-bs-table="<?= ($booking['scheduled']??false)?'scheduled':'bill'?>" data-bs-id="<?= $booking['id'] ?>" data-bs-target="#mymodalDelete"><i class="fa fa-trash"></i></a>
<?php endif; ?>
<button type="submit" class="btn btn-primary">Submit</button>
<a class="btn btn-secondary" onclick="history.back();" role="button"><i class="fa-solid fa-rotate-left"></i></a>
</div>
</div>
</div>
@@ -149,7 +135,7 @@
function openBox(e) {
e.showPicker();
}
subsdata = <?= $subsdata; ?>;
subsdata = <?= json_encode($booking['category']['subs']) ?>;
</script>
<script src="/js/billing.js"></script>
<?= $this->endSection(); ?>

View File

@@ -1,31 +1,29 @@
<?= $this->extend('layout'); ?>
<?= $this->section('css'); ?>
<link rel="stylesheet" href="/assets/jquery-ui-1.13.2.custom/jquery-ui.css">
<link rel="stylesheet" href="/css/mawim.css">
<?= $this->endSection(); ?>
<?= $this->section('menu'); ?>
<?= $this->include('sidebar') ?>
<?= $this->endSection(); ?>
<?= $this->section('content'); ?>
<?php session()->set('redirect_url',base_url('show-passwords')); ?>
<?php session()->set('redirect_url',base_url('viewScheduled')); ?>
<div class="page-content">
<div class="row">
<div class="col-xxl-8 col-12">
<div class="text-white-50"><h2>Terminbuchungen</h2></div>
<table class="table table-striped table-sm" id="scheduled" cellspacing="0"></table>
<table id="scheduled" cellspacing="0"></table>
</div>
<div class="col-xxl-4 col-12 d-none d-xxl-block">
<div class="text-white-50"><h2>Aktuelle Kontostände</h2></div>
<table class="table table-striped table-sm" id="accounts" cellspacing="0"></table>
<table id="accounts" cellspacing="0"></table>
</div>
</div>
</div>
<?= $this->endSection(); ?>
<?= $this->section('scripts'); ?>
<script src="/assets/jquery-ui-1.13.2.custom/jquery-ui.js"></script>
<script src="/js/tables.js"></script>
<script src="/js/tab_accounts.js"></script>
<script src="/js/scheduled.js"></script>
<script src="/js/tab_scheduled.js"></script>
<?= $this->endSection(); ?>

View File

@@ -30,7 +30,7 @@
</a>
</li>
<li class="sidebar-item active">
<a href="javascript:syncScheduled()" class='sidebar-link'>
<a class="sidebar-link" data-bs-toggle="modal" role="button" data-bs-target="#mySnycModal" onclick="syncScheduled(this)">
<i class="fa-solid fa-pen-to-square"></i>
<span>Sync Automatische</span>
</a>

View File

@@ -1,4 +1,4 @@
<?= $this->extend('layout'); ?>
<?= $this->extend('layout'); ?>
<?= $this->section('css'); ?>
<link rel="stylesheet" href="/css/mawim.css">
@@ -15,21 +15,21 @@
<div class="col-xl-4 col-md-8 col-sm-12">
<div class="text-white-50"><h2>Aktuelle Buchungen</h2></div>
<div class="">
<table class="table table-striped table-sm" id="bookings" cellspacing="0">
<table id="bookings" cellspacing="0">
</table>
</div>
</div>
<div class="col-xl-4 col-md-8 col-sm-12">
<div class="text-white-50"><h2>Aktuelle Kontostände</h2></div>
<div class="">
<table class="table table-striped table-sm" id="accounts" cellspacing="0">
<table id="accounts" cellspacing="0">
</table>
</div>
</div>
<div class="col-xl-4 col-md-8 col-sm-12">
<div class="text-white-50"><h2>Überfällige</h2></div>
<div class="">
<table class="table table-striped table-sm" id="todos" cellspacing="0">
<table id="todos" cellspacing="0">
</table>
</div>
</div>
@@ -40,6 +40,7 @@
<?= $this->section('scripts'); ?>
<script src="/js/tables.js"></script>
<script src="/js/tab_accounts.js"></script>
<script src="/js/tabs_overview.js"></script>
<script src="/js/tab_todos.js"></script>
<script src="/js/tab_overview.js"></script>
<?= $this->endSection(); ?>

View File

@@ -918,7 +918,9 @@
z-index: 10;
overflow-y: auto;
bottom: 0;
background-color: var(--bs-body-bg);
transition: left .5s ease-out
}
.sidebar-wrapper .sidebar-header {

View File

@@ -1,6 +1,10 @@
.dt-amount, .dt-subamount{
text-align:right;
}
.invalid-feedback{
text-align:right;
}
.dataTables_info{
float:left;
padding-top:0px !important;
@@ -10,6 +14,19 @@
float:left;
}
.datatable-sorter {
color: inherit;
background-color: inherit;
border-color: inherit;
border: 0;
padding: 0;
font-weight:700;
}
.datatable-icon {
padding-left:0 !important;
padding-right:0 !important;
}
.toolbar, .syncbutton{
float:left;
padding-left:10px;

View File

@@ -1,67 +1,71 @@
function newCat(){
$('#editor').removeClass('d-none');
$("#id").val(0);
tree = document.getElementById('tree');
editor = document.getElementById("editor");
var elementTop = $('#editor').offset().top;
var elementBottom = elementTop + $(this).outerHeight();
var viewportTop = $(window).scrollTop();
var viewportBottom = viewportTop + $(window).height();
if (!(elementBottom > viewportTop && elementTop < viewportBottom))
editor.scrollIntoView( {behavior: "smooth" });
function moveEditor(){
treepos = 90 - document.getElementById("tree").getBoundingClientRect().top;
editor.style.position = 'relative';
editor.style.top = treepos+'px';
}
function scrollToEditor(){
let editor = document.getElementById("editor");
editor.scrollIntoView( {behavior: "smooth" });
}
$(function () {
$('.tree li:has(ul)').addClass('parent_li').find(' > span');
$('.tree li.parent_li > span').on('click', function (e) {
var children = $(this).parent('li.parent_li').find(' > ul > li');
if (children.is(":visible")) {
children.hide('fast');
// $(this).find(' > i').addClass('icon-plus-sign').removeClass('icon-minus-sign');
} else {
children.show('fast');
// $(this).find(' > i').addClass('icon-minus-sign').removeClass('icon-plus-sign');
}
e.stopPropagation();
function newCat(){
document.getElementById("editor").classList.remove("d-none");
document.getElementById("id").value = 0;
document.getElementById("bookable").checked = true;
document.getElementById("account").checked = false;
document.getElementById("monitor").checked = false;
document.getElementById("deleted_at").checked = false;
document.getElementById("parent").value = 0;
document.getElementById("description").value = "";
// scrollEditor();
}
document.addEventListener("DOMContentLoaded", function(event) {
editor = document.getElementById("editor");
document.querySelectorAll('.tree li:has(ul)').forEach((single) => {
single.classList.add("parent_li");
single.getElementsByTagName("i")[0].classList.add("fa-square-minus");
});
$("span").on('click', function(e){
$('#editor').removeClass('d-none');
bookable = ($(this).attr('data-bookable') == "f")?false:true;
lkz = ($(this).attr('data-lkz') == 0)?false:true;
type = $(this).attr('data-type');
pid = $(this).attr('data-pid');
id = $(this).attr('data-id');
description = $(this).attr('data-description');
typeAccount = (type === "account") ? true : false;
typeInput = (type === "input") ? true : false;
typeOutput = (type === "output") ? true : false;
$('#bookable').prop('checked', bookable);
$('#typeInput').prop('checked', typeInput);
$('#typeOutput').prop('checked', typeOutput);
$('#typeAccount').prop('checked', typeAccount);
$('#lkz').prop('checked', lkz);
$("#parent").val(pid).change();
$("#id").val(id);
$("#description").val(description).change();
tree = document.getElementById('tree');
editor = document.getElementById("editor");
var elementTop = $('#editor').offset().top;
var elementBottom = elementTop + $(this).outerHeight();
document.querySelectorAll(".tree span").forEach((spa) => {
spa.addEventListener("dblclick", (event) => {
const viewportWidth = document.documentElement.clientWidth;
if (viewportWidth < 768)
scrollToEditor();
else
moveEditor();
var viewportTop = $(window).scrollTop();
var viewportBottom = viewportTop + $(window).height();
editor.classList.remove("d-none");
document.getElementById("bookable").checked = event.target.getAttribute("data-bookable")=="true";
document.getElementById("account").checked = event.target.getAttribute("data-account")=="true";
document.getElementById("monitor").checked = event.target.getAttribute("data-monitor")=="true";
document.getElementById("deleted_at").checked = event.target.getAttribute("data-deleted")!== "";
document.getElementById("id").value = event.target.getAttribute("data-id");
document.getElementById("parent").value = event.target.getAttribute("data-pid");
document.getElementById("description").value = event.target.getAttribute("data-description");
// scrollEditor();
});
});
document.querySelectorAll('.tree li.parent_li > span, .tree li.parent_li > i').forEach((parent) => {
parent.addEventListener("click",(event) => {
let single = event.target.parentElement.querySelector('ul,li');
let visible = single.classList.toggle("d-none");
if (visible) {
event.target.parentElement.querySelector("i").classList.add("fa-square-plus");
event.target.parentElement.querySelector("i").classList.remove("fa-square-minus");
} else {
event.target.parentElement.querySelector("i").classList.add("fa-square-minus");
event.target.parentElement.querySelector("i").classList.remove("fa-square-plus");
}
if (!(elementBottom > viewportTop && elementTop < viewportBottom))
editor.scrollIntoView( {behavior: "smooth" });
//$('#parent').prop('selected', pid);
// title: "Edit Account",
// placement: 'bottom',
// html: true,
// content: $('[data-name="popover-content"]')
});
});
document.getElementById("viewdeleted").addEventListener("click",(event) => {
document.querySelectorAll(".tree span").forEach((spa) => {
if (event.currentTarget.checked && spa.getAttribute("data-deleted")!== "")
spa.parentElement.classList.remove("d-none");
else if ((!event.currentTarget.checked) && spa.getAttribute("data-deleted")!== "")
spa.parentElement.classList.add("d-none");
});
});
});

View File

@@ -1,57 +0,0 @@
var datatable;
var colomnspez = columns;
function createSearchData(data, type, row){
let details = "";
data.forEach(function(item){
details += item.comment+' '+item.description+' ';
details += new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(item.subamount);
});
return details;
}
colomnspez.splice(3, 0, { title: "Quelle",orderable:false, data: "source",className:"dt-source" });
colomnspez.splice(6, 0, { title: "Details",orderable:false, visible:false, searchable:true, data: "details",className:"",render: createSearchData});
$(document).ready(function(){
datatable = $('#bookings').DataTable({
paging: false,
searching:true,
scrollX:false,
info:false,
order: [[ 1, 'desc' ],[ 0, 'desc' ]],
"processing": true,
ajax: {
type : 'POST',
url : '/Ajax/getTabelData',
data : function(d){
d.datum_start = $('#datepickers').val();
d.datum_ende = $('#datepickere').val();
d.source = $('#source').val();
},
dataSrc:''},
layout: {
topStart: {
buttons: [
{text:'Neue Rechnung',className:'btn-sm',action: function ( e, dt, node, config ) {
location.href = '/newBill/';
}},
{text:'Neuer Transfer',className:'btn-sm mx-2',action: function ( e, dt, node, config ) {
location.href = '/newTransfer/';
}},
]
},
topEnd: 'search'
},
// dom: '<"toolbar">frt',
"createdRow": function ( row, data, index ) {
if(data.validate == "t"){
$(row).addClass('text-bg-warning');
}
},
columns: colomnspez,
});
// $("div.toolbar").html('<a class="btn-sm btn-secondary mx-2" href="/newBill" role="button">Neue Rechnung</a><a class="btn-sm btn-secondary" href="/newTransfer" role="button">Neuer Transfer</a>');
$('#bookings tbody').on('click', 'td.dt-receiver', function () {collapseDetails($(this), datatable);} );
$('#bookings tbody').on('click', 'td.dt-datum', function () {location.href = '/editBill/'+getId($(this),datatable);});
$('#bookings tbody').on('click', 'table.tabdet', function () {location.href = '/editBill/'+$(this).attr('data-bs-id');});
});

View File

@@ -1,35 +1,146 @@
$(function () {
$('.parent').change(function () {
var $subs = $(this).parent().parent().children(".sub").children();
$subs.children().remove().end();
var $output = '';
$liste = subsdata[$(this).val()];
$sort = Object.entries($liste).sort((a, b) => (a[1] > b[1]) ? 1 : (b[1] > a[1]) ? -1 : 0);
$.each($sort, function (key, value) {
$output += '<option value="' + value[0] + '">' + value[1] + '</option>';
});
$subs.append($output);
document.addEventListener("DOMContentLoaded", function(event) {
document.myModal = document.getElementById('mymodal');
document.myModalDelete = document.getElementById('mymodalDelete');
document.myModalDelete.addEventListener('show.bs.modal', function (event) {
var button = event.relatedTarget;
var id = button.getAttribute('data-bs-id');
var tab = button.getAttribute('data-bs-table');
document.myModalDelete.setAttribute('data-bs-id',id);
document.myModalDelete.setAttribute('data-bs-table',tab);
});
document.myModal.addEventListener('show.bs.modal', function (event) {
var button = event.relatedTarget;
var id = button.getAttribute('data-bs-id');
var tab = button.getAttribute('data-bs-table');
document.myModal.setAttribute('data-bs-id',id);
document.myModal.setAttribute('data-bs-table',tab);
});
$('#multi').change(function () {
if ($(this).is(":checked")) {
$('.single').addClass('d-none');
$('.multi').removeClass('d-none');
return;
}
$('.single').removeClass('d-none');
$('.multi').addClass('d-none');
//'unchecked' event code
}).change();
$('.dt-amount, .dt-subamount').change(function () {
var temp = convertInput($(this).val());
$(this).val(convertOutput(temp));
if ($('#multi').is(":checked")) {
recalcOpenAmount();
}
}).change();
document.getElementById('modal_delete').addEventListener('click', deleteEntry );
var event = new Event('change');
switchMulti();
// dropdown elemente anpassen wenn parent sich ändert
document.querySelectorAll(".parent").forEach((papa) => {
papa.addEventListener("change", hdlDropDownChange);
});
document.querySelectorAll(".dt-amount.input,.dt-amount.output , .dt-subamount").forEach((amount) => {
amount.addEventListener("change", hdlAmountChange);
amount.dispatchEvent(event);
});
document.querySelectorAll(".dt-amount.input,.dt-amount.output,.dt-subamount, .parent").forEach((amount) => {
amount.addEventListener("focus", hdlValidityStateLine);
});
const form = document.getElementById('newbill');
form.addEventListener('formdata', function(event) {
const formData = event.formData;
formData.set('validate', formData.has('validate') ? '1' : '0');
// console.log(formData);
});
});
function loadPage(ev){
let url = ev.relatedTarget.getAttribute('url');
window.location.href="url";
}
function switchMulti(){
//const multi = document.getElementById("multi");
if (document.getElementById("linecont").childElementCount>1) {
document.getElementById("multiadd").text = "+Zeile";
const singles = document.querySelectorAll(".single").forEach((single) => {
single.classList.add("d-none");
});
const multis = document.querySelectorAll(".multi").forEach((multi) => {
multi.classList.remove("d-none");
});
// addLine();
} else {
document.getElementById("multiadd").text = "Multi";
const singles = document.querySelectorAll(".single").forEach((single) => {
single.classList.remove("d-none");
});
const multis = document.querySelectorAll(".multi").forEach((ele) => {
ele.classList.add("d-none");
});
}
}
function recalcRow(einnahme, ausgabe){
let value = einnahme.value - ausgabe.value;
if (value < 0) {
ausgabe.value = convertOutput(value *(-1));
einnahme.value = '';
} else {
einnahme.value = convertOutput(value);
ausgabe.value = '';
}
}
function hdlAmountChange(event) {
var temp = convertInput(event.target.value);
event.target.value = convertOutput(temp);
let zeile = event.target.parentElement.parentElement;
var einnahme = zeile.querySelector(".input");
var ausgabe = zeile.querySelector(".output");
if (event.target.classList.contains("input")) {
if ( ausgabe.value !== '')
recalcRow(einnahme,ausgabe);
}else {
if ( einnahme.value !== '')
recalcRow(einnahme, ausgabe);
}
calcAmount();
if (document.getElementById("linecont").childElementCount>1) {
recalcOpenAmount();
}
}
function hdlDropDownChange(event) {
var $sub = event.target.parentElement.parentElement.querySelector(".sub");
while ($sub.options.length) {
$sub.remove(0);
}
var output = '';
var liste = subsdata[(event.target.value=="")?0:event.target.value];
var sort = Object.entries(liste).sort((a, b) => (a[1] > b[1]) ? 1 : (b[1] > a[1]) ? -1 : 0);
sort.forEach((value) => {
output += '<option value="' + value[0] + '">' + value[1] + '</option>';
});
$sub.innerHTML = output;
}
function hdlValidityState(ev){
form = document.getElementById("newbill");
this.classList.remove("is-invalid");
this.setCustomValidity("");
form.classList.remove('was-validated');
}
function hdlValidityStateLine(ev){
let form = document.getElementById("newbill");
if (form.classList.contains('was-validated')) {
//ev.target.parentNode.parentNode
//document.getElementById("linecont").
form.querySelectorAll(".dt-amount, .dt-subamount, .parent, .sub").forEach((amount) => {
amount.setCustomValidity("");
amount.classList.remove("is-invalid");
amount.classList.remove("is-valid");
});
form.classList.remove('was-validated');
}
}
function logData(event) {
var data = document.getElementById("billfile");
console.log(data.value);
@@ -37,12 +148,21 @@ function logData(event) {
}
function addLine() {
$('#linecont > div').each(function () {
if ($(this).hasClass("d-none")) {
$(this).removeClass("d-none");
return false;
}
//let ele =document.querySelector("#linecont > div:last-child");
var clonedDiv = document.getElementById("template").cloneNode(true);
clonedDiv.removeAttribute("id");
clonedDiv.classList.remove("d-none");
clonedDiv.querySelector(".parent").addEventListener("change", hdlDropDownChange);
clonedDiv.querySelectorAll(".dt-subamount").forEach((amount) => {
amount.addEventListener("change", hdlAmountChange);
});
clonedDiv.querySelectorAll(".dt-subamount, .parent").forEach((amount) => {
amount.addEventListener("focus", hdlValidityStateLine);
});
document.getElementById("linecont").appendChild(clonedDiv);
if (document.getElementById("linecont").childElementCount==2){
switchMulti();
}
}
function removeBillFile(e) {
@@ -79,23 +199,97 @@ function convertOutput(number) {
return "";
}
function recalcOpenAmount() {
var summe = 0.0;
$('.dt-subamount').each(function () {
b = $(this).val();
b = convertInput(b);
function calcAmount() {
//let amount = convertInput(document.getElementsByName("amount")[0].value);
let amount = 0.0;
document.querySelectorAll(".dt-subamount").forEach((subamount) => {
var b = convertInput(subamount.value);
if (typeof b !== 'undefined') {
b = $(this).hasClass("text-danger") ? b * (-1) : b;
summe = summe + b;
b = subamount.classList.contains("output") ? b * (-1) : b;
amount = amount + b;
}
// summe = summe + convertInput($(this).val());
});
document.getElementsByName("amount")[0].value = convertOutput(amount);
$('#openamount').val(Math.round(($('#total_in').val() - $('#total_out').val() - summe) * 100) / 100);
}
// $('form').submit(function () {
// alert("disable elements");
// $( ".d-none" ).prop( "disabled", true );
// return true;
// });
function recalcOpenAmount() {
form = document.getElementById('newbill');
let summe = convertInput(document.getElementsByName("amount")[0].value);
console.log("Re calculating open amount");
var total_in = convertInput(document.getElementById("total_in").value || "0.0");
var total_out = convertInput(document.getElementById("total_out").value || "0.0");
let val = convertOutput(Math.round((total_in - total_out - summe) * 100) / 100);
if (parseFloat(val) !== 0.0){
setValState(form.openamount,"Muss 0 sein");
} else {
setValState(form.openamount,"Muss 0 sein",true);
}
document.getElementById("openamount").value = val;
}
function setValState(ele, message,valid=false){
if (valid){
ele.classList.add("is-valid");
ele.classList.remove("is-invalid");
ele.setCustomValidity("");
} else {
ele.classList.add("is-invalid");
ele.classList.remove("is-valid");
ele.setCustomValidity(message);
}
}
function checkForm(e,form) {
const subinputs = form.elements;
//for new transfer
if (form.receiver.value =='' && form.transfer.value)
form.receiver.value = subinputs['parent_id[]'].selectedOptions[0].text+"-"+subinputs['account_id[]'].selectedOptions[0].text;
if (!form.total_in.parentElement.parentElement.classList.contains("d-none")) {
//Gesamtbetrag fehlt
if ((form.total_in.value == "" ) && (form.total_out.value == "")) {
if (parseFloat(form.amount.value) > 0.0){
setValState(form.total_in, "Notwendig");
} else {
setValState(form.total_out, "Notwendig");
}
}
//Restbetrag
if (parseFloat(form.openamount.value) !== 0.0){
setValState(form.openamount, "Offener Betrag!");
form.openamount.readOnly = false;
}else{
setValState(form.openamount, "Offener Betrag!",true);
}
subinputs['account_id[]'].forEach((line,index) => {
//wenn Kategorie gewählt muss Betrag da sein
if (line.value > 0){
if (!((parseFloat(subinputs['output[]'][index].value) > 0.0) || (parseFloat(subinputs['input[]'][index].value) > 0.0))){
setValState(subinputs['output[]'][index],"Notwendig");
setValState(subinputs['input[]'][index],"Notwendig");
} else {
setValState(subinputs['output[]'][index],"Notwendig",true);
setValState(subinputs['input[]'][index],"Notwendig",true);
}
}
else if ((subinputs['output[]'][index].value !=="") || (subinputs['input[]'][index].value !=="")){
setValState(line,"Notwendig");
setValState(subinputs['parent_id[]'][index],"Notwendig");
}
else {
setValState(line,"Notwendig",true);
setValState(subinputs['parent_id[]'][index],"Notwendig",true);
}
});
}
if (!form.checkValidity()) {
e.preventDefault();
e.stopPropagation();
form.openamount.readOnly = true;
}
form.classList.add('was-validated');
}

View File

@@ -1,26 +1,66 @@
function syncScheduled(){
$('#mySnycModal').modal('show');
$('#mySnycModal').find('.modal-title').html("Synchronisierte Einträge!");
$.ajax({
type: "GET",
url: "/syncScheduled/",
data: {}, // serializes the form's elements.
success: function(data){
const myModale = document.querySelector('#mySnycModal');
var modalText = myModale.querySelector('.modal-body');
var modalFenster = myModale.querySelector('.modal-dialog');
modalText.innerHTML = data;
$('#todos').DataTable().ajax.reload();
},
error: function(data){
const myModale = document.querySelector('#mySnycModal');
var modalText = myModale.querySelector('.modal-body');
var modalFenster = myModale.querySelector('.modal-dialog');
modalFenster.classList.add("alert");
modalFenster.classList.add("alert-danger");
modalText.textContent = 'Fehler bei der Ausführung!';
$('#todos').DataTable().ajax.reload();
function syncScheduled(ele){
var myModal = document.getElementById('mySnycModal');
var modalTitle = myModal.querySelector('.modal-title');
let modalBody = myModal.querySelector('.modal-body');
modalTitle.textContent = 'Synchronisierte Einträge!';
modalBody.textContent = 'Please Wait!';
fetch("/syncScheduled", { method: "GET",headers: {"X-Requested-With": "XMLHttpRequest"},})
.then(response => response.text())
.then(data => {
const myModale = document.querySelector('#mySnycModal');
var modalBody = myModale.querySelector('.modal-body');
if (data === "") {
modalBody.innerHTML = "Nix Neues Synchronisiert!";
}
});
// location.href = '/syncScheduled/';
else
modalBody.innerHTML = data;
if (typeof loadToDosData === "function")
loadToDosData();
})
.catch(
error => {
const myModale = document.querySelector('#mySnycModal');
var modalBody = myModale.querySelector('.modal-body');
modalBody.innerHTML = "Fehler bei der Ausführung! " + error;
}
);
}
function deleteEntry(event) {
let myModalDelete = document.getElementById('mymodalDelete');
let id = myModalDelete.getAttribute('data-bs-id');
let table = myModalDelete.getAttribute('data-bs-table');
let formData = new FormData();
formData.set('id',id);
if (table == "scheduled")
cmnd = "/deleteScheduled";
else
cmnd = "/deleteBill";
fetch(cmnd, { method: "POST",headers: {"X-Requested-With": "XMLHttpRequest"}, body: formData})
.then(response => {
let myModal = document.getElementById('mymodal');
let body = myModal.querySelector('.modal-body');
if (!response.ok){
body.innerHTML = response.statusText;
}
else{
myModal.addEventListener('hidden.bs.modal', event => {
let url = event.target.getAttribute('data-bs-url');
window.location.href= url;
})
//document.getElementById('modal_ok').addEventListener('click', loadPage );
response.json().then(json => {
console.log(json);
myModal.setAttribute('data-bs-url',json.url);
body.innerHTML = "Erfolgreich gelöscht " + json.ID;
})
}
})
.catch(
error => {
let body = myModal.querySelector('.modal-body');
body.innerHTML = "Fehler bei der Ausführung! " + error;
}
);
}

View File

@@ -1,32 +0,0 @@
var datatable;
var columnspez = columns;
columnspez.splice(3, 0, { title: "Quelle",orderable:false, data: "source",className:"dt-source" });
$(document).ready(function(){
datatable = $('#scheduled').DataTable({
paging: false,
searching:true,
scrollX:false,
info:false,
order: [[ 1, 'asc' ]],
ajax: {url: '/Ajax/getScheduledData',dataSrc:''},
layout: {
topStart: {
buttons: [
{text:'Neue Rechnung',className:'btn-sm',action: function ( e, dt, node, config ) {
location.href = '/newScheduled';
}},
{text:'Neuer Transfer',className:'btn-sm mx-2',action: function ( e, dt, node, config ) {
location.href = '/newScheduledTransfer';
}},
]
},
topEnd: 'search'
},
columns: columnspez
});
$('#scheduled tbody').on('click', 'td.dt-receiver', function () {collapseDetails($(this), datatable);} );
$('#scheduled tbody').on('click', 'td.dt-datum', function () {location.href = '/editScheduled/'+getId($(this),datatable);});
$('#scheduled tbody').on('click', 'table.tabdet', function () {location.href = '/editScheduled/'+$(this).attr('data-bs-id');});
});

View File

@@ -1,16 +1,59 @@
var accounts;
$(document).ready(function(){
accounts = $('#accounts').DataTable({
var accountsdata;
function loadAccountsData(){
document.accounts.setMessage("Loading Data");
let status;
fetch("/getAccountsBalance", { method: "GET",headers: {"X-Requested-With": "XMLHttpRequest"},})
.then(response => {
status = response.statusText;
if (!response.ok) {
throw "HTTP error: " + response.status + "; " + status;
} else
return response.json();
})
.then(data => {
if (document.accounts.hasRows){
document.accounts.data.data.splice(0,document.accounts.data.data.length);
document.accounts.hasRows = false;
}
document.accounts.insert({data:data.map(item => [item.description,item.amount])});
document.accounts.update(true);
})
.catch(err => {
document.accounts.setMessage(err);
});
}
function createDtAccounts(){
document.accounts = new simpleDatatables.DataTable("#accounts", {
searchable: false,
paging: false,
searching:true,
scrollX:false,
info:false,
ajax: { url: '/Ajax/getAccountsBalance', dataSrc:''},
layout: { topEnd: 'search' },
// dom: '<"toolbar">frtip',
data: {
headings: ["Konto","Betrag"],
data: accountsdata,
},
classes: {
active: "active",
disabled: "disabled",
table: "table table-striped table-sm",
},
columns: [
{ title: "Konto",orderable:false, data: "description",className:"dt-receiver" },
colBetrag
]
{
select:0,
type: 'string',
cellClass:"dt-receiver",
},{
select:1,
type: 'number',
headerClass: "dt-amount",
cellClass:"dt-amount",
render: renderAmount
}
]
});
loadAccountsData();
}
document.addEventListener("DOMContentLoaded", function(event) {
createDtAccounts();
});

111
public/js/tab_activities.js Normal file
View File

@@ -0,0 +1,111 @@
var activitydata;
var colomnspez = columns;
function loadActivityData(){
document.activities.setMessage("Loading Data");
const form = document.querySelector("#getData");
let formData = new FormData(form);
// formData.append('start', document.getElementById("datum_start").value);
// formData.append('end', document.getElementById("datum_ende").value);
// formData.append('source', document.getElementById("source").value);
fetch("/getActivitiesData", { method: "POST",headers: {"X-Requested-With": "XMLHttpRequest"}, body: formData })
.then(response => {
let status = response.statusText;
if (!response.ok) {
throw "HTTP error: " + response.status + "; " + status;
} else
return response.json();
})
.then(data => {
if (document.activities.hasRows){
document.activities.data.data.splice(0,document.activities.data.data.length);
document.activities.hasRows = false;
}
//document.activities.insert({data:data});
document.activities.insert({data:data.map(item => [item.id,item.datum,item.receiver,item.source,item.amount,item.validate])});
document.activities.update(true);
})
.catch(err => {
document.activities.setMessage(err);
});
}
function createDtActivities(){
document.activities = new simpleDatatables.DataTable("#activities", {
searchable: true,
paging: false,
classes: {
active: "active",
disabled: "disabled",
table: "table table-striped table-sm",
},
data: {
headings: ["","Datum","Empfänger",,"Quelle","Betrag","draft"],
data: activitydata,
},
rowRender: (row, tr, _index) => {
//tr.childNodes[1].childNodes[0].attributes.href = `editBill/${row.cells[0].data}`;
tr.childNodes[1].attributes.onclick = `location.href='editBill/${row.cells[0].data}'`;
if(row.cells[5].data == true){
tr.attributes.class = 'text-bg-warning';
}
// $(row).addClass('text-bg-warning');
// tr.childNodes[2].attributes.onclick = `collapseDetails(this, ${_index})`;
},
columns: [
{
select:0,
render: (data, _cell, _dataIndex, _cellIndex) => { return `<a data-bs-toggle="modal" role="button" data-bs-id="${data}" data-bs-target="#mymodalDelete" ><i class="fa-solid fa-trash"></a>` },
cellClass: "datatable-icon",
type: "number",
},{
select:1,
type: 'date',
format: 'dd.mm.yyyy',
cellClass:"dt-datum",
sortable: true,
sort: "desc",
},{
select:2,
type: 'string',
cellClass:"dt-receiver",
// render: (data, _cell, _dataIndex, _cellIndex) => { return `<a onclick="collapseDetails(this,document.bookings)">${data}</a>` },
},{
select:3,
type: 'string',
cellClass:"dt-source",
// render: (data, _cell, _dataIndex, _cellIndex) => { return `<a onclick="collapseDetails(this,document.bookings)">${data}</a>` },
},{
select:4,
type: 'number',
headerClass: "dt-amount",
cellClass:"dt-amount",
render: renderAmount
},{
select:5,
type: 'boolean',
hidden:true,
}
],
template: (options, dom) => `<div class='${options.classes.top}'>
<div class='float-start'>
<a class="btn btn-sm btn-secondary" href="newBill">Neue Rechnung</a>
<a class="btn btn-sm btn-secondary" href="newTransfer">Neuer Transfer</a>
</div>
<div class='${options.classes.search} float-end'>
<input class='${options.classes.input}' placeholder='Search' type='search' title='${options.labels.searchTitle}'${dom.id ? ` aria-controls="${dom.id}"` : ""}>
</div>
</div>
<div class='${options.classes.container}'${options.scrollY.length ? ` style='height: ${options.scrollY}; overflow-Y: auto;'` : ""}></div>
<div class='${options.classes.bottom}'>
</div>`
});
loadActivityData();
}
document.addEventListener("DOMContentLoaded", function(event) {
createDtActivities();
});

88
public/js/tab_overview.js Normal file
View File

@@ -0,0 +1,88 @@
var bookingdata;
function createDtOverviewBookings(){
document.bookings = new simpleDatatables.DataTable("#bookings", {
searchable: false,
paging: false,
data: {
headings: ["","Datum","Empfänger","Betrag"],
data: bookingdata,
},
rowRender: (row, tr, _index) => {
//tr.childNodes[1].childNodes[0].attributes.href = `editBill/${row.cells[0].data}`;
tr.childNodes[1].attributes.onclick = `location.href='editBill/${row.cells[0].data}'`;
// tr.childNodes[2].attributes.onclick = `collapseDetails(this, ${_index})`;
},
classes: {
active: "active",
disabled: "disabled",
table: "table table-striped table-sm",
},
columns: [
{
select:0,
// render: (data, _cell, _dataIndex, _cellIndex) => { return `<a data-bs-toggle="modal" role="button" data-bs-id="'${data}'" data-bs-target="#mymodal" ><i class="fa-solid fa-trash"></a>` },
cellClass: "datatable-icon",
type: "number",
hidden:true,
},{
select:1,
type: 'date',
format: 'dd.mm.yyyy',
cellClass:"dt-datum",
sortable: true,
sort: "desc",
},{
select:2,
type: 'string',
cellClass:"dt-receiver",
// render: (data, _cell, _dataIndex, _cellIndex) => { return `<a onclick="collapseDetails(this,document.bookings)">${data}</a>` },
},{
select:3,
type: 'number',
headerClass: "dt-amount",
cellClass:"dt-amount",
render: renderAmount
}
],
template: (options, dom) => `<div class='${options.classes.top}'>
<div class='float-start'>
<a class="btn btn-sm btn-secondary" href="newBill">Neue Rechnung</a>
<a class="btn btn-sm btn-secondary" href="newTransfer">Neuer Transfer</a>
</div>
</div>
<div class='${options.classes.container}'${options.scrollY.length ? ` style='height: ${options.scrollY}; overflow-Y: auto;'` : ""}></div>
<div class='${options.classes.bottom}'>
</div>`
});
loadOverviewData();
}
function loadOverviewData(){
document.bookings.setMessage("Loading Data");
fetch("/getOverviewData", { method: "GET",headers: {"X-Requested-With": "XMLHttpRequest"},})
.then(response => {
let status = response.statusText;
if (!response.ok) {
throw "HTTP error: " + response.status + "; " + status;
} else
return response.json();
})
.then(data => {
if (document.bookings.hasRows){
document.bookings.data.data.splice(0,document.bookings.data.data.length);
document.bookings.hasRows = false;
}
//document.bookings.insert({data:data});
document.bookings.insert({data:data.map(item => [item.id,item.datum,item.receiver,item.amount])});
document.bookings.update(true);
})
.catch(err => {
document.bookings.setMessage(err);
});
}
document.addEventListener("DOMContentLoaded", function(event) {
createDtOverviewBookings();
});

View File

@@ -0,0 +1,91 @@
var scheduleddata;
var columnspez = columns;
columnspez.splice(3, 0, { title: "Quelle",orderable:false, data: "source",className:"dt-source" });
function loadScheduledData(){
document.scheduled.setMessage("Loading Data");
fetch("/getScheduledData", { method: "GET",headers: {"X-Requested-With": "XMLHttpRequest"},})
.then(response => response.json())
.then(data => {
if (document.scheduled.hasRows){
document.scheduled.data.data.splice(0,document.scheduled.data.data.length);
document.scheduled.hasRows = false;
}
//document.scheduled.insert({data:data});
document.scheduled.insert({data:data.map(item => [item.id,item.datum,item.receiver,item.source,item.amount])});
document.scheduled.update(true);
});
}
function createDtScheduledBookings(){
document.scheduled = new simpleDatatables.DataTable("#scheduled", {
searchable: true,
paging: false,
classes: {
active: "active",
disabled: "disabled",
table: "table table-striped table-sm",
},
data: {
headings: ["","Datum","Empfänger","Quelle","Betrag"],
data: scheduleddata,
},
rowRender: (row, tr, _index) => {
//tr.childNodes[1].childNodes[0].attributes.href = `editBill/${row.cells[0].data}`;
tr.childNodes[1].attributes.onclick = `location.href='editScheduled/${row.cells[0].data}'`;
// $(row).addClass('text-bg-warning');
// tr.childNodes[2].attributes.onclick = `collapseDetails(this, ${_index})`;
},
columns: [
{
select:0,
render: (data, _cell, _dataIndex, _cellIndex) => { return `<a data-bs-toggle="modal" role="button" data-bs-id="${data}" data-bs-target="#mymodalDelete" ><i class="fa-solid fa-trash"></a>` },
cellClass: "datatable-icon",
type: "number",
sort: "desc",
},{
select:1,
type: 'date',
format: 'dd.mm.yyyy',
cellClass:"dt-datum",
sortable: true,
},{
select:2,
type: 'string',
cellClass:"dt-receiver",
// render: (data, _cell, _dataIndex, _cellIndex) => { return `<a onclick="collapseDetails(this,document.bookings)">${data}</a>` },
},{
select:3,
type: 'string',
cellClass:"dt-source",
// render: (data, _cell, _dataIndex, _cellIndex) => { return `<a onclick="collapseDetails(this,document.bookings)">${data}</a>` },
},{
select:4,
type: 'number',
headerClass: "dt-amount",
cellClass:"dt-amount",
render: renderAmount
}
],
template: (options, dom) => `<div class='${options.classes.top}'>
<div class='float-start'>
<a class="btn btn-sm btn-secondary" href="newScheduled">Neue Auto Rechnung</a>
<a class="btn btn-sm btn-secondary" href="newScheduledTransfer">Neuer Auto Transfer</a>
</div>
<div class='${options.classes.search} float-end'>
<input class='${options.classes.input}' placeholder='Search' type='search' title='${options.labels.searchTitle}'${dom.id ? ` aria-controls="${dom.id}"` : ""}>
</div>
</div>
<div class='${options.classes.container}'${options.scrollY.length ? ` style='height: ${options.scrollY}; overflow-Y: auto;'` : ""}></div>
<div class='${options.classes.bottom}'>
</div>`
});
loadScheduledData();
}
document.addEventListener("DOMContentLoaded", function(event) {
createDtScheduledBookings();
})

99
public/js/tab_todos.js Normal file
View File

@@ -0,0 +1,99 @@
var todosdata;
function loadToDosData(){
document.todos.setMessage("Loading Data");
fetch("/getToDosData", { method: "GET",headers: {"X-Requested-With": "XMLHttpRequest"},})
.then(response => {
let status = response.statusText;
if (!response.ok) {
throw "HTTP error: " + response.status + "; " + status;
} else
return response.json();
})
.then(data => {
if (document.todos.hasRows){
document.todos.data.data.splice(0,document.todos.data.data.length);
document.todos.hasRows = false;
}
document.todos.insert({data:data.map(item => [item.id, item.datum, item.receiver,item.amount])});
document.todos.update(true);
})
.catch(err => {
document.todos.setMessage(err);
});
}
function createDtToDos(){
document.todos = new simpleDatatables.DataTable("#todos", {
searchable: true,
paging: true,
classes: {
active: "active",
disabled: "disabled",
selector: "form-select",
paginationList: "pagination",
paginationListItem: "page-item",
paginationListItemLink: "page-link",
table: "table table-striped table-sm",
},
data: {
headings: ["","Datum","Empfänger","Betrag"],
data: todosdata,
},
rowRender: (row, tr, _index) => {
tr.childNodes[1].attributes.onclick = `location.href='editBill/${row.cells[0].data}'`;
},
columns: [
{
select:0,
// render: (data, _cell, _dataIndex, _cellIndex) => { return `<a data-bs-toggle="modal" role="button" data-bs-id="'${data}'" data-bs-target="#mymodal" ><i class="fa-solid fa-trash"></a>` },
cellClass: "datatable-icon",
type: "number",
hidden:true,
},{
select:1,
type: 'date',
format: 'dd.mm.yyyy',
cellClass:"dt-datum",
sortable: true,
sort: "desc",
},{
select:2,
type: 'string',
cellClass:"dt-receiver",
},{
select:3,
type: 'number',
headerClass: "dt-amount",
cellClass:"dt-amount",
render: renderAmount
}
],
template: (options, dom) => `<div class='${options.classes.top}'>
<div class='float-start'>
<a class="btn btn-sm btn-secondary" data-bs-toggle="modal" role="button" data-bs-target="#mySnycModal" onclick="syncScheduled(this)">Synch</a>
</div>
<div class='${options.classes.search} float-end'>
<input class='${options.classes.input}' placeholder='Search' type='search' title='${options.labels.searchTitle}'${dom.id ? ` aria-controls="${dom.id}"` : ""}>
</div>
</div>
<div class='${options.classes.container}'${options.scrollY.length ? ` style='height: ${options.scrollY}; overflow-Y: auto;'` : ""}></div>
<div class='${options.classes.bottom}'>
<div class='float-start'>
<div class='datatable-dropdown'>
<label>
<select class='${options.classes.selector}'></select>
</label>
</div>
</div>
<div class='float-end'>
<nav class='${options.classes.pagination}'></nav>
</div>
</div>`
});
loadToDosData();
}
document.addEventListener("DOMContentLoaded", function(event) {
createDtToDos();
});

View File

@@ -1,3 +1,11 @@
const renderAmount = function(data, _cell, _dataIndex, _cellIndex) {
var ret = (Number(data)<0)?'<span style="color:red;">':'<span>';
ret += new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(data);
ret += '</span>';
return ret;
}
var colBetrag = {
title: "Betrag",
orderable:false,
@@ -47,29 +55,6 @@ function alert(alertPlaceholder, message, type) {
alertPlaceholder.append(wrapper);
}
function deleteId(id, table) {
if (table == "scheduled")
cmnd = "deleteScheduled";
else
cmnd = "deleteBill";
$.ajax({
type: "POST",
url: "/Ajax/"+cmnd,
data: {"id" : id}, // serializes the form's elements.
success: function(data){
var ph = document.getElementById('alertbox');
alert(ph,"erfolgreich gelöscht",'success');
$('#'+table).DataTable().ajax.reload();
},
error: function(data){
var ph = document.getElementById('alertbox');
alert(ph,"Fehler beim löschen",'danger');
$('#'+table).DataTable().ajax.reload();
}
});
}
function formatDetails ( d ) {
details = '';
str='';
@@ -104,20 +89,22 @@ function formatDetails ( d ) {
// null
// ] )
// } );
function collapseDetails(element, table){
var tr = element.closest('tr');
var row = table.row( tr );
function collapseDetails(e,index){
console.log(e,index);
if ( row.child.isShown() ) {
// This row is already open - close it
row.child.hide();
tr.removeClass('shown');
}
else {
// Open this row
row.child( formatDetails(row.data()) ).show();
tr.addClass('shown');
}
var tr = e.closest('tr');
// var row = table.row( tr );
// if ( row.child.isShown() ) {
// // This row is already open - close it
// row.child.hide();
// tr.removeClass('shown');
// }
// else {
// // Open this row
// row.child( formatDetails(row.data()) ).show();
// tr.addClass('shown');
// }
}
function getId(ele, table) {
@@ -126,21 +113,28 @@ function getId(ele, table) {
return row.data().id;
}
$(document).ready(function(){
document.addEventListener("DOMContentLoaded", function(event) {
var myModal = document.getElementById('mymodal');
const modal = new bootstrap.Modal(myModal);
$("#modal_ok").click(function (e) {
modal.hide();
deleteId(myModal.getAttribute('data-bs-id'), myModal.getAttribute('data-bs-table'));
var myModalDelete = document.getElementById('mymodalDelete');
myModalDelete.addEventListener('show.bs.modal', function (event) {
var button = event.relatedTarget;
var id = button.getAttribute('data-bs-id');
let tab = button.closest('table').id; // table row ID
myModalDelete.setAttribute('data-bs-id',id);
myModalDelete.setAttribute('data-bs-table',tab);
var modalTitle = myModalDelete.querySelector('.modal-title');
modalTitle.textContent = 'Lösche Eintrag Nr. ' + id;
});
myModal.addEventListener('show.bs.modal', function (event) {
var button = event.relatedTarget;
table = button.closest('table').id; // table row ID
var id = button.getAttribute('data-bs-id');
let table = button.getAttribute('data-bs-table');
myModal.setAttribute('data-bs-id',id);
myModal.setAttribute('data-bs-table',table);
var modalTitle = myModal.querySelector('.modal-title');
modalTitle.textContent = 'Lösche Eintrag Nr. ' + id;
});
document.getElementById('modal_delete').addEventListener('click', deleteEntry );
});

View File

@@ -1,54 +0,0 @@
var datatable;
var todos;
$(document).ready(function(){
datatable = $('#bookings').DataTable({
paging: false,
searching:true,
scrollX:false,
info:false,
order: [[ 1, 'desc' ]],
ajax: { url: '/Ajax/getOverviewData', dataSrc:''},
layout: {
topStart: {
buttons: [
{text:'Neue Rechnung',className:'btn-sm',action: function ( e, dt, node, config ) {
location.href = '/newBill/';
}},
]
},
topEnd: 'search'
},
// dom: '<"toolbar">frt',
columns: columns,
});
// $("div.toolbar").html('<a class="btn-sm btn-secondary" href="/newBill" role="button">Neuer Eintrag</a>');
// Add event listener for opening and closing details
$('#bookings tbody').on('click', 'td.dt-receiver', function () {collapseDetails($(this), datatable);} );
$('#bookings tbody').on('click', 'td.dt-datum', function () {location.href = '/editBill/'+getId($(this),datatable);});
$('#bookings tbody').on('click', 'table.tabdet', function () {location.href = '/editBill/'+$(this).attr('data-bs-id');});
todos = $('#todos').DataTable({
paging: true,
searching:true,
scrollX:false,
info:true,
order: [[ 1, 'desc' ]],
ajax: { url: '/Ajax/getToDosData', dataSrc:''},
layout: {
topStart: {
buttons: [
{text:'Sync',className:'btn-sm',action: syncScheduled},
]
},
topEnd: 'search'
},
// dom: '<"top"i<"syncbutton">>frt<"bottom"lp><"clear">',
columns: columns,
});
// $("div.syncbutton").html('<a class="btn-sm btn-secondary" href="/syncScheduled" role="button">Sync</a>');
// Add event listener for opening and closing details
$('#todos tbody').on('click', 'td.dt-receiver', function () {collapseDetails($(this), todos);} );
$('#todos tbody').on('click', 'td.dt-datum', function () {location.href = '/editBill/'+getId($(this),todos);});
$('#todos tbody').on('click', 'table.tabdet', function () {location.href = '/editBill/'+$(this).attr('data-bs-id');});
});