diff --git a/action.php b/action.php index d1eba2d..8ea1cb3 100644 --- a/action.php +++ b/action.php @@ -106,6 +106,34 @@ switch ($VARS['action']) { ); returnToSender("event_added", $machine->getID()); + case "editclient": + $user = new User($_SESSION['uid']); + if (!$user->hasPermission("MACHINEMANAGER_EDIT")) { + returnToSender("no_permission"); + die(); + } + + if (!Clients::areLocal()) { + returnToSender("nonlocal_client"); + } + + if (Client::exists($VARS["id"])) { + $client = new Client($VARS["id"]); + } else { + $client = new Client(); + } + + $client->setName($VARS["name"]); + $client->setPhone($VARS["phone"]); + $client->setEmail($VARS["email"]); + $client->setBillingAddress($VARS["billingaddress"]); + $client->setMailingAddress($VARS["mailingaddress"]); + $client->setPublicNotes($VARS["publicnotes"]); + $client->setPrivateNotes($VARS["privatenotes"]); + + $client->save(); + + returnToSender("client_edited", $client->getID()); case "signout": session_destroy(); header('Location: index.php?logout=1'); diff --git a/database.mwb b/database.mwb index 5270172..acd1974 100644 Binary files a/database.mwb and b/database.mwb differ diff --git a/langs/en/actions.json b/langs/en/actions.json index 11d9668..0fb4def 100644 --- a/langs/en/actions.json +++ b/langs/en/actions.json @@ -4,5 +4,6 @@ "View Schedule": "View Schedule", "View Machines": "View Machines", "Add Event": "Add Event", - "Add Component": "Add Component" + "Add Component": "Add Component", + "Add Client": "Add Client" } diff --git a/langs/en/clients.json b/langs/en/clients.json new file mode 100644 index 0000000..e8ea170 --- /dev/null +++ b/langs/en/clients.json @@ -0,0 +1,7 @@ +{ + "Name": "Name", + "Phone": "Phone", + "Email": "Email", + "Billing Address": "Billing Address", + "Mailing Address": "Mailing Address" +} \ No newline at end of file diff --git a/langs/en/labels.json b/langs/en/labels.json new file mode 100644 index 0000000..33e0142 --- /dev/null +++ b/langs/en/labels.json @@ -0,0 +1,4 @@ +{ + "Public Notes": "Public Notes", + "Private Notes": "Private Notes" +} \ No newline at end of file diff --git a/langs/en/messages.json b/langs/en/messages.json index 28d14e2..58b866d 100644 --- a/langs/en/messages.json +++ b/langs/en/messages.json @@ -1,5 +1,7 @@ { "Machine saved!": "Machine saved!", "Component saved!": "Component saved!", - "Event logged!": "Event logged!" + "Event logged!": "Event logged!", + "Client saved!": "Client saved!", + "Client must be edited in Invoice Ninja.": "Client must be edited in Invoice Ninja." } diff --git a/langs/en/titles.json b/langs/en/titles.json index 6649a77..aa8e1b2 100644 --- a/langs/en/titles.json +++ b/langs/en/titles.json @@ -2,5 +2,6 @@ "Home": "Home", "Form": "Form", "Machines": "Machines", + "Clients": "Clients", "Machine Info": "Machine Info" } diff --git a/langs/messages.php b/langs/messages.php index 3e11270..888045a 100644 --- a/langs/messages.php +++ b/langs/messages.php @@ -29,6 +29,14 @@ define("MESSAGES", [ "string" => "Event logged!", "type" => "success" ], + "client_edited" => [ + "string" => "Client saved!", + "type" => "success" + ], + "nonlocal_client" => [ + "string" => "Client must be edited in Invoice Ninja.", + "type" => "danger" + ], "404_error" => [ "string" => "page not found", "type" => "info" diff --git a/lib/Client.lib.php b/lib/Client.lib.php index 184ac7a..f8a4ffb 100644 --- a/lib/Client.lib.php +++ b/lib/Client.lib.php @@ -7,22 +7,231 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use InvoiceNinja\Config as NinjaConfig; +use InvoiceNinja\Models\Client as NinjaClient; class Client { - public $id = null; - public $name = null; + private $local = true; + private $exists = false; + private $full = true; + private $id = ""; + private $name = ""; + private $phone = ""; + private $email = ""; + private $billingaddress = ""; + private $mailingaddress = ""; + private $publicnotes = ""; + private $privatenotes = ""; - public function __construct($id, $name) { - $this->id = $id; - $this->name = $name; + /** + * + * @param type $id + * @param type $local + * @param type $full + * @param type $name + */ + public function __construct($id = "", $local = true, $name = "") { + global $database; + if (!empty($id)) { + $this->id = $id; + $this->local = $local; + $this->exists = true; + if ($local) { + $this->full = true; + $data = $database->get("clients", ['name', 'phone', 'email', 'billingaddress', 'mailingaddress', 'publicnotes', 'privatenotes'], ["clientid" => $id]); + $this->setName($data['name'] . ""); + $this->setPhone($data['phone'] . ""); + $this->setEmail($data['email'] . ""); + $this->setBillingAddress($data['billingaddress'] . ""); + $this->setMailingAddress($data['mailingaddress'] . ""); + $this->setPublicNotes($data['publicnotes'] . ""); + $this->setPrivateNotes($data['privatenotes'] . ""); + } else { + $this->full = false; + $this->name = $name; + } + } + } + + /** + * Fill in all the client data from the InvoiceNinja API. + */ + private function fullerize() { + global $SETTINGS; + if (!$this->local && !$this->full && $this->exists) { + try { + $client = NinjaClient::find($this->id); + $this->full = true; + // Name + $this->setName($client->display_name); + // Phone + if (!empty($client->work_phone)) { + $this->setPhone($client->work_phone); + } else if (!empty($client->contacts[0]->phone)) { + $this->setPhone($client->contacts[0]->phone); + } + if (!empty($client->contacts[0]->email)) { + $this->setEmail($client->contacts[0]->email); + } + $billingaddress = [ + $client->address1, + $client->address2, + implode(" ", array_filter([$client->city, $client->state, $client->postal_code])) + ]; + $this->setBillingAddress(implode("\n", array_filter($billingaddress))); + + $mailingaddress = [ + $client->shipping_address1, + $client->shipping_address2, + implode(" ", array_filter([$client->shipping_city, $client->shipping_state, $client->shipping_postal_code])) + ]; + $this->setMailingAddress(implode("\n", array_filter($mailingaddress))); + + $this->setPublicNotes($client->public_notes); + $this->setPrivateNotes($client->private_notes); + } catch (Exception $ex) { + if ($SETTINGS['debug']) { + echo $ex->getTraceAsString(); + } + sendError("Unable to query client from InvoiceNinja server:\n" . $ex->getMessage()); + } + } + } + + public static function exists($id, $local = true): bool { + global $database, $SETTINGS; + if ($local) { + return $database->has('clients', ['clientid' => $id]); + } + try { + $client = NinjaClient::find($id); + return true; + } catch (Exception $ex) { + if ($ex->getMessage() == "{\n \"message\": \"record does not exist\"\n}") { + return false; + } + if ($SETTINGS['debug']) { + echo $ex->getTraceAsString(); + } + sendError("Unable to query client ID from InvoiceNinja server:\n" . $ex->getMessage()); + } + } + + /** + * Save the client. + */ + public function save() { + global $database; + if ($this->isLocal()) { + $data = [ + "name" => $this->getName(), + "phone" => $this->getPhone(), + "email" => $this->getEmail(), + "billingaddress" => $this->getBillingAddress(), + "mailingaddress" => $this->getMailingAddress(), + "publicnotes" => $this->getPublicNotes(), + "privatenotes" => $this->getPrivateNotes() + ]; + if (!empty($this->id) && $database->has("clients", ["clientid" => $this->id])) { + $database->update("clients", $data, ["clientid" => $this->id]); + } else { + $database->insert("clients", $data); + $this->id = $database->id(); + } + return; + } + + if ($this->exists) { + $client = NinjaClient::find($id); + $client->name = $this->getName(); + } else { + $client = new NinjaClient($this->getEmail(), '', '', $this->getName()); + } + $client->work_phone = $this->getPhone(); + $client->public_notes = $this->getPublicNotes(); + $client->private_notes = $this->getPrivateNotes(); + $client->save(); + } + + public function isLocal(): bool { + return $this->local; } public function getID() { return $this->id; } - public function getName() { + public function getName(): string { + if (empty($this->name)) { + $this->fullerize(); + } return $this->name; } -} \ No newline at end of file + + public function getPhone(): string { + $this->fullerize(); + return $this->phone; + } + + public function getEmail(): string { + $this->fullerize(); + return $this->email; + } + + public function getBillingAddress(): string { + $this->fullerize(); + return $this->billingaddress; + } + + public function getMailingAddress(): string { + $this->fullerize(); + return $this->mailingaddress; + } + + public function getPublicNotes(): string { + $this->fullerize(); + return $this->publicnotes; + } + + public function getPrivateNotes(): string { + $this->fullerize(); + return $this->privatenotes; + } + + public function setName(string $name) { + $this->fullerize(); + $this->name = $name; + } + + public function setPhone(string $phone) { + $this->fullerize(); + $this->phone = $phone; + } + + public function setEmail(string $email) { + $this->fullerize(); + $this->email = $email; + } + + public function setBillingAddress(string $address) { + $this->fullerize(); + $this->billingaddress = $address; + } + + public function setMailingAddress(string $address) { + $this->fullerize(); + $this->mailingaddress = $address; + } + + public function setPublicNotes(string $notes) { + $this->fullerize(); + $this->publicnotes = $notes; + } + + public function setPrivateNotes(string $notes) { + $this->fullerize(); + $this->privatenotes = $notes; + } + +} diff --git a/lib/Clients.lib.php b/lib/Clients.lib.php index 1a51b89..2ccac8b 100644 --- a/lib/Clients.lib.php +++ b/lib/Clients.lib.php @@ -19,49 +19,59 @@ if (!empty($SETTINGS["apis"]["invoiceninja"]["token"])) { if ($SETTINGS['debug']) { echo $ex->getTraceAsString(); } - sendError("Unable to load client list from InvoiceNinja server:\n" . $ex->getMessage()); + sendError("Unable to load InvoiceNinja API:\n" . $ex->getMessage()); } class Clients { public static function getAll(): array { - $clients = NinjaClient::all(); + try { + $clients = NinjaClient::all(); + } catch (Exception $ex) { + if ($SETTINGS['debug']) { + echo $ex->getTraceAsString(); + } + sendError("Unable to get InvoiceNinja client list:\n" . $ex->getMessage()); + } $list = []; foreach ($clients as $client) { - $name = $client->name; - if (empty($name)) { - $name = $client->contacts[0]->first_name . " " . $client->contacts[0]->last_name; - } - $list[] = new Client($client->id, $name); + $list[] = new Client($client->id, false, $client->display_name); } return $list; } public static function getClient($id): Client { - $client = NinjaClient::find($id); - return new Client($client->id, $client->name); + return new Client($client->id, false); + } + + public static function areLocal(): bool { + return false; } } } else { + // Use internal client table class Clients { public static function getAll(): array { global $database; - $clients = $database->select("clients", ["clientid", "name"]); + $clients = $database->select("clients", ["clientid"]); $list = []; foreach ($clients as $client) { - $list[] = new Client($client['clientid'], $client['name']); + $list[] = new Client($client['clientid'], true); } return $list; } public static function getClient($id): Client { global $database; - $client = $database->get("clients", ["clientid", "name"], ["clientid" => $id]); - return new Client($client['clientid'], $client['name']); + return new Client($id, true); + } + + public static function areLocal(): bool { + return true; } } diff --git a/pages.php b/pages.php index a70e4ee..0632d94 100644 --- a/pages.php +++ b/pages.php @@ -25,6 +25,19 @@ define("PAGES", [ "static/js/machines.js" ] ], + "clients" => [ + "title" => "Clients", + "navbar" => (empty($SETTINGS['apis']['invoiceninja']['token']) ? true : false), + "icon" => "fas fa-users", + "styles" => [ + "static/css/datatables.min.css", + "static/css/tables.css" + ], + "scripts" => [ + "static/js/datatables.min.js", + "static/js/clients.js" + ] + ], "404" => [ "title" => "404 error" ], @@ -39,5 +52,8 @@ define("PAGES", [ ], "editcomponent" => [ "title" => "Edit Component" - ] + ], + "editclient" => [ + "title" => "Edit Client" + ], ]); \ No newline at end of file diff --git a/pages/clients.php b/pages/clients.php new file mode 100644 index 0000000..96df6c8 --- /dev/null +++ b/pages/clients.php @@ -0,0 +1,94 @@ +hasPermission("MACHINEMANAGER_VIEW")) { + header("Location: ./app.php?msg=no_permission"); + die(); +} + +$writeaccess = $user->hasPermission("MACHINEMANAGER_EDIT"); + +$clients = Clients::getAll(); +?> + +
+ + get("Add Client"); ?> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
get('Name'); ?> get('Phone'); ?> get('Email'); ?> get('Billing Address'); ?> get('Mailing Address'); ?> get('Public Notes'); ?> get('Private Notes'); ?>
+ isLocal() && $writeaccess) { + ?> + get("Edit"); ?> + getName()); ?> + + getName()); ?> + + + getPhone())) { ?> + getPhone())); ?>"> + getPhone()); ?> + + + + getEmail())) { ?> + + getEmail()); ?> + + + ", explode("\n", $c->getBillingAddress())); ?>", explode("\n", $c->getMailingAddress())); ?>getPublicNotes()); ?>getPrivateNotes()); ?>
get('Name'); ?> get('Phone'); ?> get('Email'); ?> get('Billing Address'); ?> get('Mailing Address'); ?> get('Public Notes'); ?> get('Private Notes'); ?>
\ No newline at end of file diff --git a/pages/editclient.php b/pages/editclient.php new file mode 100644 index 0000000..8bbf037 --- /dev/null +++ b/pages/editclient.php @@ -0,0 +1,50 @@ +hasPermission("MACHINEMANAGER_EDIT")) { + header("Location: ./app.php?msg=no_permission"); + die(); +} + +$editing = false; + +if (!empty($_GET['arg']) && Client::exists($_GET['arg'], Clients::areLocal())) { + $editing = true; + $client = new Client($_GET['arg'], Clients::areLocal()); +} else { + $client = new Client(); +} + +if ($editing) { + $form = new FormBuilder("Edit " . htmlspecialchars($client->getName()), "fas fa-user", "action.php", "POST"); +} else { + $form = new FormBuilder("Add Client", "fas fa-user", "action.php", "POST"); +} + +$form->setID("editclient"); + +$form->addHiddenInput("action", "editclient"); +$form->addHiddenInput("source", "editclient"); + +if ($editing) { + $form->addHiddenInput("id", $client->getID()); +} + +$form->addInput("name", $client->getName(), "text", true, null, null, "Name", "fas fa-user"); +$form->addInput("phone", $client->getPhone(), "tel", false, null, null, "Phone", "fas fa-phone"); +$form->addInput("email", $client->getEmail(), "email", false, null, null, "Email", "fas fa-envelope"); +$form->addInput("billingaddress", $client->getBillingAddress(), "textarea", false, null, null, "Billing Address", "fas fa-file-invoice", 6); +$form->addInput("mailingaddress", $client->getMailingAddress(), "textarea", false, null, null, "Mailing Address", "fas fa-mail-bulk", 6); +$form->addInput("privatenotes", $client->getPrivateNotes(), "textarea", false, null, null, "Private Notes", "fas fa-comment-dots", 6); +$form->addInput("publicnotes", $client->getPublicNotes(), "textarea", false, null, null, "Public Notes", "far fa-comment-dots", 6); + +$form->addButton("Save", "fas fa-save", null, "submit", "savebtn"); + +$form->generate(); diff --git a/static/js/clients.js b/static/js/clients.js new file mode 100644 index 0000000..4db2152 --- /dev/null +++ b/static/js/clients.js @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +$('#clienttable').DataTable({ + responsive: { + details: { + display: $.fn.dataTable.Responsive.display.modal({ + header: function (row) { + var data = row.data(); + return " " + data[2]; + } + }), + renderer: $.fn.dataTable.Responsive.renderer.tableAll({ + tableClass: 'table' + }), + type: "column" + } + }, + columnDefs: [ + { + targets: 0, + className: 'control', + orderable: false + } + ], + order: [ + [4, 'desc'] + ] +}); \ No newline at end of file