From 404435a145d66209eedbcd2cbf547bbf13bd20bb Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 28 Oct 2015 21:22:07 +0200 Subject: [PATCH] Refactored to events --- app/Commands/Command.php | 7 +- app/Console/Commands/CheckData.php | 3 +- app/Console/Commands/CreateRandomData.php | 88 ---- app/Console/Kernel.php | 1 - app/Events/ClientWasArchived.php | 21 + app/Events/ClientWasCreated.php | 21 + app/Events/ClientWasDeleted.php | 21 + app/Events/ClientWasRestored.php | 21 + app/Events/ClientWasUpdated.php | 21 + app/Events/CreditWasArchived.php | 23 + app/Events/CreditWasCreated.php | 23 + app/Events/CreditWasDeleted.php | 23 + app/Events/CreditWasRestored.php | 23 + app/Events/InvoiceInvitationWasEmailed.php | 25 + app/Events/InvoiceInvitationWasViewed.php | 25 + app/Events/InvoiceWasArchived.php | 22 + ...{InvoiceSent.php => InvoiceWasCreated.php} | 3 +- ...nvoiceViewed.php => InvoiceWasDeleted.php} | 11 +- app/Events/InvoiceWasEmailed.php | 22 + app/Events/InvoiceWasRestored.php | 22 + ...uoteApproved.php => InvoiceWasUpdated.php} | 3 +- ...InvoicePaid.php => PaymentWasArchived.php} | 3 +- app/Events/PaymentWasCreated.php | 22 + app/Events/PaymentWasDeleted.php | 22 + app/Events/PaymentWasRestored.php | 22 + app/Events/QuoteInvitationWasApproved.php | 25 + app/Events/QuoteInvitationWasEmailed.php | 25 + app/Events/QuoteInvitationWasViewed.php | 25 + app/Events/QuoteWasArchived.php | 22 + app/Events/QuoteWasCreated.php | 22 + app/Events/QuoteWasDeleted.php | 22 + app/Events/QuoteWasEmailed.php | 22 + app/Events/QuoteWasRestored.php | 22 + app/Events/QuoteWasUpdated.php | 22 + app/Http/Controllers/AccountController.php | 79 +-- app/Http/Controllers/ActivityController.php | 38 +- app/Http/Controllers/BaseController.php | 4 + app/Http/Controllers/ClientApiController.php | 23 +- app/Http/Controllers/ClientController.php | 119 +---- app/Http/Controllers/CreditController.php | 49 +- app/Http/Controllers/DashboardController.php | 1 + app/Http/Controllers/InvoiceApiController.php | 10 +- app/Http/Controllers/InvoiceController.php | 180 +++---- app/Http/Controllers/PaymentApiController.php | 2 +- app/Http/Controllers/PaymentController.php | 58 +-- .../Controllers/PublicClientController.php | 23 +- app/Http/Controllers/QuoteController.php | 38 +- app/Http/Controllers/TaskController.php | 4 +- app/Http/Requests/CreateClientRequest.php | 44 ++ app/Http/Requests/CreateCreditRequest.php | 30 ++ app/Http/Requests/CreatePaymentRequest.php | 44 ++ app/Http/Requests/SaveInvoiceRequest.php | 44 ++ app/Http/Requests/UpdateClientRequest.php | 29 ++ app/Http/Requests/UpdatePaymentRequest.php | 28 + app/Http/routes.php | 7 +- app/Libraries/Utils.php | 59 ++- app/Listeners/ActivityListener.php | 337 ++++++++++++ app/Listeners/CreditListener.php | 33 ++ app/Listeners/HandleInvoicePaid.php | 48 -- app/Listeners/HandleInvoiceSent.php | 42 -- app/Listeners/HandleInvoiceViewed.php | 42 -- app/Listeners/HandleQuoteApproved.php | 42 -- app/Listeners/HandleUserSignedUp.php | 6 - app/Listeners/InvoiceListener.php | 49 ++ app/Listeners/NotificationListener.php | 71 +++ app/Listeners/QuoteListener.php | 19 + app/Listeners/SubscriptionListener.php | 47 ++ app/Listeners/TaskListener.php | 14 + app/Models/Account.php | 19 +- app/Models/Activity.php | 486 ++---------------- app/Models/BalanceAffecting.php | 6 + app/Models/Client.php | 93 +++- app/Models/Contact.php | 8 + app/Models/Credit.php | 27 +- app/Models/EntityModel.php | 19 +- app/Models/Invitation.php | 12 + app/Models/Invoice.php | 124 ++++- app/Models/Payment.php | 26 +- app/Models/Task.php | 18 +- app/Ninja/Mailers/ContactMailer.php | 20 +- app/Ninja/Repositories/ActivityRepository.php | 102 ++++ app/Ninja/Repositories/BaseRepository.php | 58 +++ app/Ninja/Repositories/ClientRepository.php | 181 +------ app/Ninja/Repositories/CreditRepository.php | 38 +- app/Ninja/Repositories/InvoiceRepository.php | 116 ++--- app/Ninja/Repositories/PaymentRepository.php | 64 +-- app/Providers/AppServiceProvider.php | 13 + app/Providers/EventServiceProvider.php | 130 ++++- app/Services/BaseService.php | 28 + app/Services/ClientService.php | 25 + app/Services/CreditService.php | 25 + app/Services/InvoiceService.php | 81 +++ app/Services/PaymentService.php | 18 +- ..._27_180214_add_is_system_to_activities.php | 51 ++ resources/lang/da/texts.php | 57 ++ resources/lang/de/texts.php | 44 ++ resources/lang/en/texts.php | 34 ++ resources/lang/en/validation.php | 1 + resources/lang/es/texts.php | 56 ++ resources/lang/es_ES/texts.php | 56 ++ resources/lang/fr/texts.php | 58 ++- resources/lang/fr_CA/texts.php | 55 ++ resources/lang/it/texts.php | 56 ++ resources/lang/lt/texts.php | 56 ++ resources/lang/nb_NO/texts.php | 56 ++ resources/lang/nl/texts.php | 56 ++ resources/lang/pt_BR/texts.php | 56 ++ resources/lang/sv/texts.php | 108 +++- resources/views/clients/edit.blade.php | 41 +- resources/views/clients/show.blade.php | 2 +- resources/views/dashboard.blade.php | 2 +- resources/views/invoices/edit.blade.php | 200 ++++--- resources/views/invoices/knockout.blade.php | 3 +- resources/views/list.blade.php | 16 +- resources/views/payments/edit.blade.php | 3 + resources/views/tasks/edit.blade.php | 5 +- tests/acceptance/CheckBalanceCest.php | 8 +- tests/acceptance/ClientCest.php | 8 +- tests/acceptance/CreditCest.php | 2 +- tests/acceptance/InvoiceCest.php | 4 +- tests/acceptance/OnlinePaymentCest.php | 5 +- tests/acceptance/PaymentCest.php | 4 +- 122 files changed, 3516 insertions(+), 1692 deletions(-) delete mode 100644 app/Console/Commands/CreateRandomData.php create mode 100644 app/Events/ClientWasArchived.php create mode 100644 app/Events/ClientWasCreated.php create mode 100644 app/Events/ClientWasDeleted.php create mode 100644 app/Events/ClientWasRestored.php create mode 100644 app/Events/ClientWasUpdated.php create mode 100644 app/Events/CreditWasArchived.php create mode 100644 app/Events/CreditWasCreated.php create mode 100644 app/Events/CreditWasDeleted.php create mode 100644 app/Events/CreditWasRestored.php create mode 100644 app/Events/InvoiceInvitationWasEmailed.php create mode 100644 app/Events/InvoiceInvitationWasViewed.php create mode 100644 app/Events/InvoiceWasArchived.php rename app/Events/{InvoiceSent.php => InvoiceWasCreated.php} (88%) rename app/Events/{InvoiceViewed.php => InvoiceWasDeleted.php} (61%) create mode 100644 app/Events/InvoiceWasEmailed.php create mode 100644 app/Events/InvoiceWasRestored.php rename app/Events/{QuoteApproved.php => InvoiceWasUpdated.php} (87%) rename app/Events/{InvoicePaid.php => PaymentWasArchived.php} (87%) create mode 100644 app/Events/PaymentWasCreated.php create mode 100644 app/Events/PaymentWasDeleted.php create mode 100644 app/Events/PaymentWasRestored.php create mode 100644 app/Events/QuoteInvitationWasApproved.php create mode 100644 app/Events/QuoteInvitationWasEmailed.php create mode 100644 app/Events/QuoteInvitationWasViewed.php create mode 100644 app/Events/QuoteWasArchived.php create mode 100644 app/Events/QuoteWasCreated.php create mode 100644 app/Events/QuoteWasDeleted.php create mode 100644 app/Events/QuoteWasEmailed.php create mode 100644 app/Events/QuoteWasRestored.php create mode 100644 app/Events/QuoteWasUpdated.php create mode 100644 app/Http/Requests/CreateClientRequest.php create mode 100644 app/Http/Requests/CreateCreditRequest.php create mode 100644 app/Http/Requests/CreatePaymentRequest.php create mode 100644 app/Http/Requests/SaveInvoiceRequest.php create mode 100644 app/Http/Requests/UpdateClientRequest.php create mode 100644 app/Http/Requests/UpdatePaymentRequest.php create mode 100644 app/Listeners/ActivityListener.php create mode 100644 app/Listeners/CreditListener.php delete mode 100644 app/Listeners/HandleInvoicePaid.php delete mode 100644 app/Listeners/HandleInvoiceSent.php delete mode 100644 app/Listeners/HandleInvoiceViewed.php delete mode 100644 app/Listeners/HandleQuoteApproved.php create mode 100644 app/Listeners/InvoiceListener.php create mode 100644 app/Listeners/NotificationListener.php create mode 100644 app/Listeners/QuoteListener.php create mode 100644 app/Listeners/SubscriptionListener.php create mode 100644 app/Listeners/TaskListener.php create mode 100644 app/Models/BalanceAffecting.php create mode 100644 app/Ninja/Repositories/ActivityRepository.php create mode 100644 app/Ninja/Repositories/BaseRepository.php create mode 100644 app/Services/BaseService.php create mode 100644 app/Services/ClientService.php create mode 100644 app/Services/CreditService.php create mode 100644 app/Services/InvoiceService.php create mode 100644 database/migrations/2015_10_27_180214_add_is_system_to_activities.php diff --git a/app/Commands/Command.php b/app/Commands/Command.php index 018bc2192..5bc485011 100644 --- a/app/Commands/Command.php +++ b/app/Commands/Command.php @@ -1,7 +1,6 @@ -where('client_id', '=', $client->id) ->orderBy('activities.id') - ->get(['activities.id', 'activities.created_at', 'activities.activity_type_id', 'activities.message', 'activities.adjustment', 'activities.balance', 'activities.invoice_id']); + ->get(['activities.id', 'activities.created_at', 'activities.activity_type_id', 'activities.adjustment', 'activities.balance', 'activities.invoice_id']); //$this->info(var_dump($activities)); foreach ($activities as $activity) { @@ -235,7 +235,6 @@ class CheckData extends Command { 'updated_at' => new Carbon, 'account_id' => $client->account_id, 'client_id' => $client->id, - 'message' => 'Corrected client balance', 'adjustment' => $client->actual_balance - $activity->balance, 'balance' => $client->actual_balance, ]); diff --git a/app/Console/Commands/CreateRandomData.php b/app/Console/Commands/CreateRandomData.php deleted file mode 100644 index de4da39cb..000000000 --- a/app/Console/Commands/CreateRandomData.php +++ /dev/null @@ -1,88 +0,0 @@ -info(date('Y-m-d') . ' Running CreateRandomData...'); - - $user = User::first(); - - if (!$user) { - $this->error("Error: please create user account by logging in"); - return; - } - - $productNames = ['Arkansas', 'New York', 'Arizona', 'California', 'Colorado', 'Alabama', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'Alaska', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming']; - $clientNames = ['IBM', 'Nestle', 'Mitsubishi UFJ Financial', 'Vodafone', 'Eni', 'Procter & Gamble', 'Johnson & Johnson', 'American International Group', 'Banco Santander', 'BHP Billiton', 'Pfizer', 'Itaú Unibanco Holding', 'Ford Motor', 'BMW Group', 'Commonwealth Bank', 'EDF', 'Statoil', 'Google', 'Siemens', 'Novartis', 'Royal Bank of Canada', 'Sumitomo Mitsui Financial', 'Comcast', 'Sberbank', 'Goldman Sachs Group', 'Westpac Banking Group', 'Nippon Telegraph & Tel', 'Ping An Insurance Group', 'Banco Bradesco', 'Anheuser-Busch InBev', 'Bank of Communications', 'China Life Insurance', 'General Motors', 'Telefónica', 'MetLife', 'Honda Motor', 'Enel', 'BASF', 'Softbank', 'National Australia Bank', 'ANZ', 'ConocoPhillips', 'TD Bank Group', 'Intel', 'UBS', 'Hewlett-Packard', 'Coca-Cola', 'Cisco Systems', 'UnitedHealth Group', 'Boeing', 'Zurich Insurance Group', 'Hyundai Motor', 'Sanofi', 'Credit Agricole', 'United Technologies', 'Roche Holding', 'Munich Re', 'PepsiCo', 'Oracle', 'Bank of Nova Scotia']; - - foreach ($productNames as $i => $value) { - $product = Product::createNew($user); - $product->id = $i+1; - $product->product_key = $value; - $product->save(); - } - - foreach ($clientNames as $i => $value) { - $client = Client::createNew($user); - $client->name = $value; - $client->save(); - - $contact = Contact::createNew($user); - $contact->email = "client@aol.com"; - $contact->is_primary = 1; - $client->contacts()->save($contact); - - $numInvoices = rand(1, 25); - if ($numInvoices == 4 || $numInvoices == 10 || $numInvoices == 25) { - // leave these - } else if ($numInvoices % 3 == 0) { - $numInvoices = 1; - } else if ($numInvoices > 10) { - $numInvoices = $numInvoices % 2; - } - - $paidUp = rand(0, 1) == 1; - - for ($j=1; $j<=$numInvoices; $j++) { - - $price = rand(10, 1000); - if ($price < 900) { - $price = rand(10, 150); - } - - $invoice = Invoice::createNew($user); - $invoice->invoice_number = $user->account->getNextInvoiceNumber($invoice); - $invoice->amount = $invoice->balance = $price; - $invoice->created_at = date('Y-m-d', strtotime(date("Y-m-d") . ' - ' . rand(1, 100) . ' days')); - $client->invoices()->save($invoice); - - $productId = rand(0, 40); - if ($productId > 20) { - $productId = ($productId % 2) + rand(0, 2); - } - - $invoiceItem = InvoiceItem::createNew($user); - $invoiceItem->product_id = $productId+1; - $invoiceItem->product_key = $productNames[$invoiceItem->product_id]; - $invoiceItem->cost = $invoice->amount; - $invoiceItem->qty = 1; - $invoice->invoice_items()->save($invoiceItem); - - if ($paidUp || rand(0,2) > 1) { - $payment = Payment::createNew($user); - $payment->invoice_id = $invoice->id; - $payment->amount = $invoice->amount; - $client->payments()->save($payment); - } - } - } - } -} \ No newline at end of file diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index fd97865be..03b6ce776 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -13,7 +13,6 @@ class Kernel extends ConsoleKernel */ protected $commands = [ 'App\Console\Commands\SendRecurringInvoices', - 'App\Console\Commands\CreateRandomData', 'App\Console\Commands\ResetData', 'App\Console\Commands\CheckData', 'App\Console\Commands\SendRenewalInvoices', diff --git a/app/Events/ClientWasArchived.php b/app/Events/ClientWasArchived.php new file mode 100644 index 000000000..03ebdc09c --- /dev/null +++ b/app/Events/ClientWasArchived.php @@ -0,0 +1,21 @@ +client = $client; + } +} diff --git a/app/Events/ClientWasCreated.php b/app/Events/ClientWasCreated.php new file mode 100644 index 000000000..5c2d37001 --- /dev/null +++ b/app/Events/ClientWasCreated.php @@ -0,0 +1,21 @@ +client = $client; + } +} diff --git a/app/Events/ClientWasDeleted.php b/app/Events/ClientWasDeleted.php new file mode 100644 index 000000000..b87063c49 --- /dev/null +++ b/app/Events/ClientWasDeleted.php @@ -0,0 +1,21 @@ +client = $client; + } +} diff --git a/app/Events/ClientWasRestored.php b/app/Events/ClientWasRestored.php new file mode 100644 index 000000000..385a0472a --- /dev/null +++ b/app/Events/ClientWasRestored.php @@ -0,0 +1,21 @@ +client = $client; + } +} diff --git a/app/Events/ClientWasUpdated.php b/app/Events/ClientWasUpdated.php new file mode 100644 index 000000000..7e4790da6 --- /dev/null +++ b/app/Events/ClientWasUpdated.php @@ -0,0 +1,21 @@ +client = $client; + } +} diff --git a/app/Events/CreditWasArchived.php b/app/Events/CreditWasArchived.php new file mode 100644 index 000000000..2c680905b --- /dev/null +++ b/app/Events/CreditWasArchived.php @@ -0,0 +1,23 @@ +credit = $credit; + } + +} diff --git a/app/Events/CreditWasCreated.php b/app/Events/CreditWasCreated.php new file mode 100644 index 000000000..bc20b312d --- /dev/null +++ b/app/Events/CreditWasCreated.php @@ -0,0 +1,23 @@ +credit = $credit; + } + +} diff --git a/app/Events/CreditWasDeleted.php b/app/Events/CreditWasDeleted.php new file mode 100644 index 000000000..e26a5d3ab --- /dev/null +++ b/app/Events/CreditWasDeleted.php @@ -0,0 +1,23 @@ +credit = $credit; + } + +} diff --git a/app/Events/CreditWasRestored.php b/app/Events/CreditWasRestored.php new file mode 100644 index 000000000..8d17d961e --- /dev/null +++ b/app/Events/CreditWasRestored.php @@ -0,0 +1,23 @@ +credit = $credit; + } + +} diff --git a/app/Events/InvoiceInvitationWasEmailed.php b/app/Events/InvoiceInvitationWasEmailed.php new file mode 100644 index 000000000..1a602c867 --- /dev/null +++ b/app/Events/InvoiceInvitationWasEmailed.php @@ -0,0 +1,25 @@ +invoice = $invoice; + $this->invitation = $invitation; + } + +} diff --git a/app/Events/InvoiceInvitationWasViewed.php b/app/Events/InvoiceInvitationWasViewed.php new file mode 100644 index 000000000..bbf7e23c3 --- /dev/null +++ b/app/Events/InvoiceInvitationWasViewed.php @@ -0,0 +1,25 @@ +invoice = $invoice; + $this->invitation = $invitation; + } + +} diff --git a/app/Events/InvoiceWasArchived.php b/app/Events/InvoiceWasArchived.php new file mode 100644 index 000000000..7587c071a --- /dev/null +++ b/app/Events/InvoiceWasArchived.php @@ -0,0 +1,22 @@ +invoice = $invoice; + } + +} diff --git a/app/Events/InvoiceSent.php b/app/Events/InvoiceWasCreated.php similarity index 88% rename from app/Events/InvoiceSent.php rename to app/Events/InvoiceWasCreated.php index cbe08d052..cfd943bcf 100644 --- a/app/Events/InvoiceSent.php +++ b/app/Events/InvoiceWasCreated.php @@ -4,10 +4,9 @@ use App\Events\Event; use Illuminate\Queue\SerializesModels; -class InvoiceSent extends Event { +class InvoiceWasCreated extends Event { use SerializesModels; - public $invoice; /** diff --git a/app/Events/InvoiceViewed.php b/app/Events/InvoiceWasDeleted.php similarity index 61% rename from app/Events/InvoiceViewed.php rename to app/Events/InvoiceWasDeleted.php index 8d9f129e7..316b1b5c5 100644 --- a/app/Events/InvoiceViewed.php +++ b/app/Events/InvoiceWasDeleted.php @@ -4,10 +4,9 @@ use App\Events\Event; use Illuminate\Queue\SerializesModels; -class InvoiceViewed extends Event { +class InvoiceWasDeleted extends Event { use SerializesModels; - public $invoice; /** @@ -15,9 +14,9 @@ class InvoiceViewed extends Event { * * @return void */ - public function __construct($invoice) - { - $this->invoice = $invoice; - } + public function __construct($invoice) + { + $this->invoice = $invoice; + } } diff --git a/app/Events/InvoiceWasEmailed.php b/app/Events/InvoiceWasEmailed.php new file mode 100644 index 000000000..dc30f6a55 --- /dev/null +++ b/app/Events/InvoiceWasEmailed.php @@ -0,0 +1,22 @@ +invoice = $invoice; + } + +} diff --git a/app/Events/InvoiceWasRestored.php b/app/Events/InvoiceWasRestored.php new file mode 100644 index 000000000..35c646612 --- /dev/null +++ b/app/Events/InvoiceWasRestored.php @@ -0,0 +1,22 @@ +invoice = $invoice; + } + +} diff --git a/app/Events/QuoteApproved.php b/app/Events/InvoiceWasUpdated.php similarity index 87% rename from app/Events/QuoteApproved.php rename to app/Events/InvoiceWasUpdated.php index 12b5384b3..87a0f8f20 100644 --- a/app/Events/QuoteApproved.php +++ b/app/Events/InvoiceWasUpdated.php @@ -4,10 +4,9 @@ use App\Events\Event; use Illuminate\Queue\SerializesModels; -class QuoteApproved extends Event { +class InvoiceWasUpdated extends Event { use SerializesModels; - public $invoice; /** diff --git a/app/Events/InvoicePaid.php b/app/Events/PaymentWasArchived.php similarity index 87% rename from app/Events/InvoicePaid.php rename to app/Events/PaymentWasArchived.php index 4dced7347..b8bb693df 100644 --- a/app/Events/InvoicePaid.php +++ b/app/Events/PaymentWasArchived.php @@ -4,10 +4,9 @@ use App\Events\Event; use Illuminate\Queue\SerializesModels; -class InvoicePaid extends Event { +class PaymentWasArchived extends Event { use SerializesModels; - public $payment; /** diff --git a/app/Events/PaymentWasCreated.php b/app/Events/PaymentWasCreated.php new file mode 100644 index 000000000..619d33e95 --- /dev/null +++ b/app/Events/PaymentWasCreated.php @@ -0,0 +1,22 @@ +payment = $payment; + } + +} diff --git a/app/Events/PaymentWasDeleted.php b/app/Events/PaymentWasDeleted.php new file mode 100644 index 000000000..e12647c86 --- /dev/null +++ b/app/Events/PaymentWasDeleted.php @@ -0,0 +1,22 @@ +payment = $payment; + } + +} diff --git a/app/Events/PaymentWasRestored.php b/app/Events/PaymentWasRestored.php new file mode 100644 index 000000000..b4198aab4 --- /dev/null +++ b/app/Events/PaymentWasRestored.php @@ -0,0 +1,22 @@ +payment = $payment; + } + +} diff --git a/app/Events/QuoteInvitationWasApproved.php b/app/Events/QuoteInvitationWasApproved.php new file mode 100644 index 000000000..954aa83b4 --- /dev/null +++ b/app/Events/QuoteInvitationWasApproved.php @@ -0,0 +1,25 @@ +quote = $quote; + $this->invitation = $invitation; + } + +} diff --git a/app/Events/QuoteInvitationWasEmailed.php b/app/Events/QuoteInvitationWasEmailed.php new file mode 100644 index 000000000..ce1b87d4a --- /dev/null +++ b/app/Events/QuoteInvitationWasEmailed.php @@ -0,0 +1,25 @@ +quote = $quote; + $this->invitation = $invitation; + } + +} diff --git a/app/Events/QuoteInvitationWasViewed.php b/app/Events/QuoteInvitationWasViewed.php new file mode 100644 index 000000000..3cd84b0e1 --- /dev/null +++ b/app/Events/QuoteInvitationWasViewed.php @@ -0,0 +1,25 @@ +quote = $quote; + $this->invitation = $invitation; + } + +} diff --git a/app/Events/QuoteWasArchived.php b/app/Events/QuoteWasArchived.php new file mode 100644 index 000000000..285a61250 --- /dev/null +++ b/app/Events/QuoteWasArchived.php @@ -0,0 +1,22 @@ +quote = $quote; + } + +} diff --git a/app/Events/QuoteWasCreated.php b/app/Events/QuoteWasCreated.php new file mode 100644 index 000000000..d17ef9c13 --- /dev/null +++ b/app/Events/QuoteWasCreated.php @@ -0,0 +1,22 @@ +quote = $quote; + } + +} diff --git a/app/Events/QuoteWasDeleted.php b/app/Events/QuoteWasDeleted.php new file mode 100644 index 000000000..ce3685d7a --- /dev/null +++ b/app/Events/QuoteWasDeleted.php @@ -0,0 +1,22 @@ +quote = $quote; + } + +} diff --git a/app/Events/QuoteWasEmailed.php b/app/Events/QuoteWasEmailed.php new file mode 100644 index 000000000..19b1ec12d --- /dev/null +++ b/app/Events/QuoteWasEmailed.php @@ -0,0 +1,22 @@ +quote = $quote; + } + +} diff --git a/app/Events/QuoteWasRestored.php b/app/Events/QuoteWasRestored.php new file mode 100644 index 000000000..0f13a65b4 --- /dev/null +++ b/app/Events/QuoteWasRestored.php @@ -0,0 +1,22 @@ +quote = $quote; + } + +} diff --git a/app/Events/QuoteWasUpdated.php b/app/Events/QuoteWasUpdated.php new file mode 100644 index 000000000..f01b98226 --- /dev/null +++ b/app/Events/QuoteWasUpdated.php @@ -0,0 +1,22 @@ +quote = $quote; + } + +} diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index d4a358ebc..843107673 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -45,6 +45,8 @@ use App\Events\UserLoggedIn; use App\Events\UserSettingsChanged; use App\Services\AuthService; +use App\Commands\CreateClient; + class AccountController extends BaseController { protected $accountRepo; @@ -637,49 +639,56 @@ class AccountController extends BaseController continue; } - $client = Client::createNew(); - $contact = Contact::createNew(); - $contact->is_primary = true; - $contact->send_invoice = true; - $count++; + $data = [ + 'contacts' => [[]] + ]; foreach ($row as $index => $value) { $field = $map[$index]; - $value = trim($value); + if ( ! $value = trim($value)) { + continue; + } - if ($field == Client::$fieldName && !$client->name) { - $client->name = $value; - } elseif ($field == Client::$fieldPhone && !$client->work_phone) { - $client->work_phone = $value; - } elseif ($field == Client::$fieldAddress1 && !$client->address1) { - $client->address1 = $value; - } elseif ($field == Client::$fieldAddress2 && !$client->address2) { - $client->address2 = $value; - } elseif ($field == Client::$fieldCity && !$client->city) { - $client->city = $value; - } elseif ($field == Client::$fieldState && !$client->state) { - $client->state = $value; - } elseif ($field == Client::$fieldPostalCode && !$client->postal_code) { - $client->postal_code = $value; - } elseif ($field == Client::$fieldCountry && !$client->country_id) { + if ($field == Client::$fieldName) { + $data['name'] = $value; + } elseif ($field == Client::$fieldPhone) { + $data['work_phone'] = $value; + } elseif ($field == Client::$fieldAddress1) { + $data['address1'] = $value; + } elseif ($field == Client::$fieldAddress2) { + $data['address2'] = $value; + } elseif ($field == Client::$fieldCity) { + $data['city'] = $value; + } elseif ($field == Client::$fieldState) { + $data['state'] = $value; + } elseif ($field == Client::$fieldPostalCode) { + $data['postal_code'] = $value; + } elseif ($field == Client::$fieldCountry) { $value = strtolower($value); - $client->country_id = isset($countryMap[$value]) ? $countryMap[$value] : null; - } elseif ($field == Client::$fieldNotes && !$client->private_notes) { - $client->private_notes = $value; - } elseif ($field == Contact::$fieldFirstName && !$contact->first_name) { - $contact->first_name = $value; - } elseif ($field == Contact::$fieldLastName && !$contact->last_name) { - $contact->last_name = $value; - } elseif ($field == Contact::$fieldPhone && !$contact->phone) { - $contact->phone = $value; - } elseif ($field == Contact::$fieldEmail && !$contact->email) { - $contact->email = strtolower($value); + $data['country_id'] = isset($countryMap[$value]) ? $countryMap[$value] : null; + } elseif ($field == Client::$fieldNotes) { + $data['private_notes'] = $value; + } elseif ($field == Contact::$fieldFirstName) { + $data['contacts'][0]['first_name'] = $value; + } elseif ($field == Contact::$fieldLastName) { + $data['contacts'][0]['last_name'] = $value; + } elseif ($field == Contact::$fieldPhone) { + $data['contacts'][0]['phone'] = $value; + } elseif ($field == Contact::$fieldEmail) { + $data['contacts'][0]['email'] = strtolower($value); } } - $client->save(); - $client->contacts()->save($contact); - Activity::createClient($client, false); + $rules = [ + 'contacts' => 'valid_contacts', + ]; + $validator = Validator::make($data, $rules); + if ($validator->fails()) { + continue; + } + + $this->dispatch(new CreateClient($data)); + $count++; } $message = Utils::pluralize('created_client', $count); diff --git a/app/Http/Controllers/ActivityController.php b/app/Http/Controllers/ActivityController.php index 39419f227..02700e233 100644 --- a/app/Http/Controllers/ActivityController.php +++ b/app/Http/Controllers/ActivityController.php @@ -5,20 +5,46 @@ use DB; use Datatable; use Utils; use View; +use App\Models\Client; +use App\Models\Activity; +use App\Ninja\Repositories\ActivityRepository; class ActivityController extends BaseController { + protected $activityRepo; + + public function __construct(ActivityRepository $activityRepo) + { + parent::__construct(); + + $this->activityRepo = $activityRepo; + } + public function getDatatable($clientPublicId) { - $query = DB::table('activities') - ->join('clients', 'clients.id', '=', 'activities.client_id') - ->where('clients.public_id', '=', $clientPublicId) - ->where('activities.account_id', '=', Auth::user()->account_id) - ->select('activities.id', 'activities.message', 'activities.created_at', 'clients.currency_id', 'activities.balance', 'activities.adjustment'); + $clientId = Client::getPrivateId($clientPublicId); + + if ( ! $clientId) { + app()->abort(404); + } + + $query = $this->activityRepo->findByClientId($clientId); return Datatable::query($query) ->addColumn('activities.id', function ($model) { return Utils::timestampToDateTimeString(strtotime($model->created_at)); }) - ->addColumn('message', function ($model) { return Utils::decodeActivity($model->message); }) + ->addColumn('activity_type_id', function ($model) { + $data = [ + 'client' => link_to('/clients/' . $model->client_public_id, Utils::getClientDisplayName($model)), + 'user' => $model->is_system ? '' . trans('texts.system') . '' : Utils::getPersonDisplayName($model->user_first_name, $model->user_last_name, $model->user_email), + 'invoice' => $model->invoice ? link_to('/invoices/' . $model->invoice_public_id, $model->is_recurring ? trans('texts.recurring_invoice') : $model->invoice) : null, + 'quote' => $model->invoice ? link_to('/quotes/' . $model->invoice_public_id, $model->invoice) : null, + 'contact' => $model->contact_id ? link_to('/clients/' . $model->client_public_id, Utils::getClientDisplayName($model)) : Utils::getPersonDisplayName($model->user_first_name, $model->user_last_name, $model->user_email), + 'payment' => $model->payment ?: '', + 'credit' => Utils::formatMoney($model->credit, $model->currency_id) + ]; + + return trans("texts.activity_{$model->activity_type_id}", $data); + }) ->addColumn('balance', function ($model) { return Utils::formatMoney($model->balance, $model->currency_id); }) ->addColumn('adjustment', function ($model) { return $model->adjustment != 0 ? Utils::wrapAdjustment($model->adjustment, $model->currency_id) : ''; }) ->make(); diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 0cc63c7c5..1a2f6c8dc 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -1,7 +1,11 @@ clientRepo->getErrors($data); + $client = $this->clientRepo->save($request->input()); - if ($error) { - $headers = Utils::getApiHeaders(); + $client = Client::scope($client->public_id)->with('country', 'contacts', 'industry', 'size', 'currency')->first(); + $client = Utils::remapPublicIds([$client]); + $response = json_encode($client, JSON_PRETTY_PRINT); + $headers = Utils::getApiHeaders(); - return Response::make($error, 500, $headers); - } else { - $client = $this->clientRepo->save(isset($data['id']) ? $data['id'] : false, $data, false); - $client = Client::scope($client->public_id)->with('country', 'contacts', 'industry', 'size', 'currency')->first(); - $client = Utils::remapPublicIds([$client]); - $response = json_encode($client, JSON_PRETTY_PRINT); - $headers = Utils::getApiHeaders(); - - return Response::make($response, 200, $headers); - } + return Response::make($response, 200, $headers); } } diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 6b168751c..badcdfc35 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -21,18 +21,23 @@ use App\Models\Industry; use App\Models\Currency; use App\Models\Country; use App\Models\Task; - use App\Ninja\Repositories\ClientRepository; +use App\Services\ClientService; + +use App\Http\Requests\CreateClientRequest; +use App\Http\Requests\UpdateClientRequest; class ClientController extends BaseController { + protected $clientService; protected $clientRepo; - public function __construct(ClientRepository $clientRepo) + public function __construct(ClientRepository $clientRepo, ClientService $clientService) { parent::__construct(); $this->clientRepo = $clientRepo; + $this->clientService = $clientService; } /** @@ -103,9 +108,13 @@ class ClientController extends BaseController * * @return Response */ - public function store() + public function store(CreateClientRequest $request) { - return $this->save(); + $client = $this->clientService->save($request->input()); + + Session::flash('message', trans('texts.created_client')); + + return redirect()->to($client->getRoute()); } /** @@ -194,6 +203,7 @@ class ClientController extends BaseController private static function getViewModel() { return [ + 'data' => Input::old('data'), 'account' => Auth::user()->account, 'sizes' => Cache::get('sizes'), 'paymentTerms' => Cache::get('paymentTerms'), @@ -212,105 +222,20 @@ class ClientController extends BaseController * @param int $id * @return Response */ - public function update($publicId) + public function update(UpdateClientRequest $request) { - return $this->save($publicId); - } - - private function save($publicId = null) - { - $rules = array( - 'email' => 'email|required_without:first_name', - 'first_name' => 'required_without:email', - ); - $validator = Validator::make(Input::all(), $rules); - - if ($validator->fails()) { - $url = $publicId ? 'clients/'.$publicId.'/edit' : 'clients/create'; - - return Redirect::to($url) - ->withErrors($validator) - ->withInput(Input::except('password')); - } else { - if ($publicId) { - $client = Client::scope($publicId)->firstOrFail(); - } else { - $client = Client::createNew(); - } - - $client->name = trim(Input::get('name')); - $client->id_number = trim(Input::get('id_number')); - $client->vat_number = trim(Input::get('vat_number')); - $client->work_phone = trim(Input::get('work_phone')); - $client->custom_value1 = trim(Input::get('custom_value1')); - $client->custom_value2 = trim(Input::get('custom_value2')); - $client->address1 = trim(Input::get('address1')); - $client->address2 = trim(Input::get('address2')); - $client->city = trim(Input::get('city')); - $client->state = trim(Input::get('state')); - $client->postal_code = trim(Input::get('postal_code')); - $client->country_id = Input::get('country_id') ?: null; - $client->private_notes = trim(Input::get('private_notes')); - $client->size_id = Input::get('size_id') ?: null; - $client->industry_id = Input::get('industry_id') ?: null; - $client->currency_id = Input::get('currency_id') ?: null; - $client->language_id = Input::get('language_id') ?: null; - $client->payment_terms = Input::get('payment_terms') ?: 0; - $client->website = trim(Input::get('website')); - - if (Input::has('invoice_number_counter')) { - $client->invoice_number_counter = (int) Input::get('invoice_number_counter'); - } - if (Input::has('quote_number_counter')) { - $client->invoice_number_counter = (int) Input::get('quote_number_counter'); - } - - $client->save(); - - $data = json_decode(Input::get('data')); - $contactIds = []; - $isPrimary = true; - - foreach ($data->contacts as $contact) { - if (isset($contact->public_id) && $contact->public_id) { - $record = Contact::scope($contact->public_id)->firstOrFail(); - } else { - $record = Contact::createNew(); - } - - $record->email = trim($contact->email); - $record->first_name = trim($contact->first_name); - $record->last_name = trim($contact->last_name); - $record->phone = trim($contact->phone); - $record->is_primary = $isPrimary; - $isPrimary = false; - - $client->contacts()->save($record); - $contactIds[] = $record->public_id; - } - - foreach ($client->contacts as $contact) { - if (!in_array($contact->public_id, $contactIds)) { - $contact->delete(); - } - } - - if ($publicId) { - Session::flash('message', trans('texts.updated_client')); - } else { - Activity::createClient($client); - Session::flash('message', trans('texts.created_client')); - } - - return Redirect::to('clients/'.$client->public_id); - } + $client = $this->clientService->save($request->input()); + + Session::flash('message', trans('texts.updated_client')); + + return redirect()->to($client->getRoute()); } public function bulk() { $action = Input::get('action'); - $ids = Input::get('id') ? Input::get('id') : Input::get('ids'); - $count = $this->clientRepo->bulk($ids, $action); + $ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids'); + $count = $this->clientService->bulk($ids, $action); $message = Utils::pluralize($action.'d_client', $count); Session::flash('message', $message); diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 505400718..ad04651ac 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -8,18 +8,21 @@ use Utils; use View; use Validator; use App\Models\Client; - +use App\Services\CreditService; use App\Ninja\Repositories\CreditRepository; +use App\Http\Requests\CreateCreditRequest; class CreditController extends BaseController { protected $creditRepo; + protected $CreditService; - public function __construct(CreditRepository $creditRepo) + public function __construct(CreditRepository $creditRepo, CreditService $creditService) { parent::__construct(); $this->creditRepo = $creditRepo; + $this->creditService = $creditService; } /** @@ -106,46 +109,20 @@ class CreditController extends BaseController return View::make('credit.edit', $data); } - public function store() + public function store(CreateCreditRequest $request) { - return $this->save(); - } - - public function update($publicId) - { - return $this->save($publicId); - } - - private function save($publicId = null) - { - $rules = array( - 'client' => 'required', - 'amount' => 'required|positive', - ); - - $validator = Validator::make(Input::all(), $rules); - - if ($validator->fails()) { - $url = $publicId ? 'credits/'.$publicId.'/edit' : 'credits/create'; - - return Redirect::to($url) - ->withErrors($validator) - ->withInput(); - } else { - $this->creditRepo->save($publicId, Input::all()); - - $message = trans('texts.created_credit'); - Session::flash('message', $message); - - return Redirect::to('clients/'.Input::get('client')); - } + $credit = $this->creditRepo->save($request->input()); + + Session::flash('message', trans('texts.created_credit')); + + return redirect()->to($credit->client->getRoute()); } public function bulk() { $action = Input::get('action'); - $ids = Input::get('id') ? Input::get('id') : Input::get('ids'); - $count = $this->creditRepo->bulk($ids, $action); + $ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids'); + $count = $this->creditService->bulk($ids, $action); if ($count > 0) { $message = Utils::pluralize($action.'d_credit', $count); diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index 0b72d3669..a8b31fdb8 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -62,6 +62,7 @@ class DashboardController extends BaseController ->get(); $activities = Activity::where('activities.account_id', '=', Auth::user()->account_id) + ->with('client.contacts', 'user', 'invoice', 'payment', 'credit') ->where('activity_type_id', '>', 0) ->orderBy('created_at', 'desc') ->take(50) diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index d3bb9b44b..2a9bae80e 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -58,7 +58,11 @@ class InvoiceApiController extends Controller { $data = Input::all(); $error = null; - + + if (isset($data['id']) || isset($data['public_id'])) { + die("We don't yet support updating invoices"); + } + if (isset($data['email'])) { $client = Client::scope()->whereHas('contacts', function($query) use ($data) { $query->where('email', '=', $data['email']); @@ -78,7 +82,7 @@ class InvoiceApiController extends Controller } $error = $this->clientRepo->getErrors($clientData); if (!$error) { - $client = $this->clientRepo->save(false, $clientData, false); + $client = $this->clientRepo->save($clientData); } } } else if (isset($data['client_id'])) { @@ -108,7 +112,7 @@ class InvoiceApiController extends Controller } else { $data = self::prepareData($data, $client); $data['client_id'] = $client->id; - $invoice = $this->invoiceRepo->save(false, $data, false); + $invoice = $this->invoiceRepo->save($data); if (!isset($data['id'])) { $invitation = Invitation::createNew(); diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 54d74258c..a8fb60a77 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -14,7 +14,6 @@ use Datatable; use Request; use DropdownButton; use App\Models\Invoice; -use App\Models\Invitation; use App\Models\Client; use App\Models\Account; use App\Models\Product; @@ -31,21 +30,27 @@ use App\Models\Gateway; use App\Ninja\Mailers\ContactMailer as Mailer; use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\ClientRepository; -use App\Events\InvoiceViewed; +use App\Events\InvoiceInvitationWasViewed; +use App\Events\QuoteInvitationWasViewed; + +use App\Services\InvoiceService; +use App\Http\Requests\SaveInvoiceRequest; class InvoiceController extends BaseController { protected $mailer; protected $invoiceRepo; protected $clientRepo; + protected $invoiceService; - public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo) + public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService) { parent::__construct(); $this->mailer = $mailer; $this->invoiceRepo = $invoiceRepo; $this->clientRepo = $clientRepo; + $this->invoiceService = $invoiceService; } public function index() @@ -147,12 +152,15 @@ class InvoiceController extends BaseController } if (!Input::has('phantomjs') && !Session::has($invitationKey) && (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) { - Activity::viewInvoice($invitation); - Event::fire(new InvoiceViewed($invoice)); + if ($invoice->is_quote) { + event(new QuoteInvitationWasViewed($invoice, $invitation)); + } else { + event(new InvoiceInvitationWasViewed($invoice, $invitation)); + } } - Session::set($invitationKey, true); // track this invitation has been seen - Session::set('invitation_key', $invitationKey); // track current invitation + Session::put($invitationKey, true); // track this invitation has been seen + Session::put('invitation_key', $invitationKey); // track current invitation $account->loadLocalizationSettings($client); @@ -245,7 +253,7 @@ class InvoiceController extends BaseController ->select('contacts.public_id')->lists('public_id'); if ($clone) { - $invoice->id = null; + $invoice->id = $invoice->public_id = null; $invoice->invoice_number = $account->getNextInvoiceNumber($invoice); $invoice->balance = $invoice->amount; $invoice->invoice_status_id = 0; @@ -347,13 +355,16 @@ class InvoiceController extends BaseController public function create($clientPublicId = 0, $isRecurring = false) { $account = Auth::user()->account; + $entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE; $clientId = null; + if ($clientPublicId) { $clientId = Client::getPrivateId($clientPublicId); } - $entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE; - $invoice = $account->createInvoice($entityType, $clientId); + $invoice = $account->createInvoice($entityType, $clientId); + $invoice->public_id = 0; + $data = [ 'entityType' => $invoice->getEntityType(), 'invoice' => $invoice, @@ -418,50 +429,62 @@ class InvoiceController extends BaseController * * @return Response */ - public function store() - { - return InvoiceController::save(); - } - - private function save($publicId = null) + public function store(SaveInvoiceRequest $request) { $action = Input::get('action'); $entityType = Input::get('entityType'); - $input = json_decode(Input::get('data')); - if (in_array($action, ['archive', 'delete', 'mark', 'restore'])) { - return InvoiceController::bulk($entityType); + $invoice = $this->invoiceService->save($request->input()); + $entityType = $invoice->getEntityType(); + $message = trans("texts.created_{$entityType}"); + + // check if we created a new client with the invoice + // TODO: replace with HistoryListener + $input = $request->input(); + $clientPublicId = isset($input['client']['public_id']) ? $input['client']['public_id'] : false; + if ($clientPublicId == '-1') { + $message = $message.' '.trans('texts.and_created_client'); + $trackUrl = URL::to('clients/' . $invoice->client->public_id); + Utils::trackViewed($invoice->client->getDisplayName(), ENTITY_CLIENT, $trackUrl); } - if ($errors = $this->invoiceRepo->getErrors($input->invoice)) { - Session::flash('error', trans('texts.invoice_error')); - $url = "{$entityType}s/" . ($publicId ?: 'create'); - return Redirect::to($url)->withInput()->withErrors($errors); - } else { - $invoice = $this->saveInvoice($publicId, $input, $entityType); - $url = "{$entityType}s/".$invoice->public_id.'/edit'; - $message = trans($publicId ? "texts.updated_{$entityType}" : "texts.created_{$entityType}"); + Session::flash('message', $message); - // check if we created a new client with the invoice - if ($input->invoice->client->public_id == '-1') { - $message = $message.' '.trans('texts.and_created_client'); - $trackUrl = URL::to('clients/'.$invoice->client->public_id); - Utils::trackViewed($invoice->client->getDisplayName(), ENTITY_CLIENT, $trackUrl); - } - - if ($action == 'clone') { - return $this->cloneInvoice($publicId); - } elseif ($action == 'convert') { - return $this->convertQuote($publicId); - } elseif ($action == 'email') { - return $this->emailInvoice($invoice, Input::get('pdfupload')); - } - - Session::flash('message', $message); - return Redirect::to($url); + if ($action == 'email') { + return $this->emailInvoice($invoice, Input::get('pdfupload')); } + + return redirect()->to($invoice->getRoute()); } + /** + * Update the specified resource in storage. + * + * @param int $id + * @return Response + */ + public function update(SaveInvoiceRequest $request) + { + $action = Input::get('action'); + $entityType = Input::get('entityType'); + + $invoice = $this->invoiceService->save($request->input()); + $entityType = $invoice->getEntityType(); + $message = trans("texts.updated_{$entityType}"); + Session::flash('message', $message); + + if ($action == 'clone') { + return $this->cloneInvoice($invoice->public_id); + } elseif ($action == 'convert') { + return $this->convertQuote($invoice->public_id); + } elseif ($action == 'email') { + return $this->emailInvoice($invoice, Input::get('pdfupload')); + } + + return redirect()->to($invoice->getRoute()); + } + + private function emailInvoice($invoice, $pdfUpload) { $entityType = $invoice->getEntityType(); @@ -512,43 +535,6 @@ class InvoiceController extends BaseController } } - private function saveInvoice($publicId, $input, $entityType) - { - $invoice = $input->invoice; - - $clientData = (array) $invoice->client; - $client = $this->clientRepo->save($invoice->client->public_id, $clientData); - - $invoiceData = (array) $invoice; - $invoiceData['client_id'] = $client->id; - $invoice = $this->invoiceRepo->save($publicId, $invoiceData, $entityType); - - $client->load('contacts'); - $sendInvoiceIds = []; - - foreach ($client->contacts as $contact) { - if ($contact->send_invoice || count($client->contacts) == 1) { - $sendInvoiceIds[] = $contact->id; - } - } - - foreach ($client->contacts as $contact) { - $invitation = Invitation::scope()->whereContactId($contact->id)->whereInvoiceId($invoice->id)->first(); - - if (in_array($contact->id, $sendInvoiceIds) && !$invitation) { - $invitation = Invitation::createNew(); - $invitation->invoice_id = $invoice->id; - $invitation->contact_id = $contact->id; - $invitation->invitation_key = str_random(RANDOM_KEY_LENGTH); - $invitation->save(); - } elseif (!in_array($contact->id, $sendInvoiceIds) && $invitation) { - $invitation->delete(); - } - } - - return $invoice; - } - /** * Display the specified resource. * @@ -562,17 +548,6 @@ class InvoiceController extends BaseController return Redirect::to("invoices/{$publicId}/edit"); } - /** - * Update the specified resource in storage. - * - * @param int $id - * @return Response - */ - public function update($publicId) - { - return InvoiceController::save($publicId); - } - /** * Remove the specified resource from storage. * @@ -581,10 +556,10 @@ class InvoiceController extends BaseController */ public function bulk($entityType = ENTITY_INVOICE) { - $action = Input::get('action'); + $action = Input::get('bulk_action') ?: Input::get('action');; + $ids = Input::get('bulk_public_id') ?: (Input::get('public_id') ?: Input::get('ids')); $statusId = Input::get('statusId', INVOICE_STATUS_SENT); - $ids = Input::get('id') ? Input::get('id') : Input::get('ids'); - $count = $this->invoiceRepo->bulk($ids, $action, $statusId); + $count = $this->invoiceService->bulk($ids, $action, $statusId); if ($count > 0) { $key = $action == 'mark' ? "updated_{$entityType}" : "{$action}d_{$entityType}"; @@ -602,7 +577,7 @@ class InvoiceController extends BaseController public function convertQuote($publicId) { $invoice = Invoice::with('invoice_items')->scope($publicId)->firstOrFail(); - $clone = $this->invoiceRepo->cloneInvoice($invoice, $invoice->id); + $clone = $this->invoiceService->approveQuote($invoice); Session::flash('message', trans('texts.converted_to_invoice')); return Redirect::to('invoices/'.$clone->public_id); @@ -610,15 +585,6 @@ class InvoiceController extends BaseController public function cloneInvoice($publicId) { - /* - $invoice = Invoice::with('invoice_items')->scope($publicId)->firstOrFail(); - $clone = $this->invoiceRepo->cloneInvoice($invoice); - $entityType = $invoice->getEntityType(); - - Session::flash('message', trans('texts.cloned_invoice')); - return Redirect::to("{$entityType}s/" . $clone->public_id); - */ - return self::edit($publicId, true); } @@ -636,7 +602,7 @@ class InvoiceController extends BaseController ->where('activity_type_id', '=', $activityTypeId) ->where('invoice_id', '=', $invoice->id) ->orderBy('id', 'desc') - ->get(['id', 'created_at', 'user_id', 'json_backup', 'message']); + ->get(['id', 'created_at', 'user_id', 'json_backup']); $versionsJson = []; $versionsSelect = []; @@ -651,7 +617,7 @@ class InvoiceController extends BaseController $backup->account = $invoice->account->toArray(); $versionsJson[$activity->id] = $backup; - $key = Utils::timestampToDateTimeString(strtotime($activity->created_at)) . ' - ' . Utils::decodeActivity($activity->message); + $key = Utils::timestampToDateTimeString(strtotime($activity->created_at)) . ' - ' . $activity->user->getDisplayName(); $versionsSelect[$lastId ? $lastId : 0] = $key; $lastId = $activity->id; } diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index 17d2e548f..d83fcac49 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -60,7 +60,7 @@ class PaymentApiController extends Controller } if (!$error) { - $payment = $this->paymentRepo->save(false, $data); + $payment = $this->paymentRepo->save($data); $payment = Payment::scope($payment->public_id)->with('client', 'contact', 'user', 'invoice')->first(); $payment = Utils::remapPublicIds([$payment]); diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 84a140551..0ede064b4 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -25,6 +25,9 @@ use App\Ninja\Repositories\AccountRepository; use App\Ninja\Mailers\ContactMailer; use App\Services\PaymentService; +use App\Http\Requests\CreatePaymentRequest; +use App\Http\Requests\UpdatePaymentRequest; + class PaymentController extends BaseController { public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService) @@ -540,49 +543,36 @@ class PaymentController extends BaseController } } - public function store() + public function store(CreatePaymentRequest $request) { - return $this->save(); - } + $input = $request->input(); + $payment = $this->paymentRepo->save($input); - public function update($publicId) - { - return $this->save($publicId); - } - - private function save($publicId = null) - { - if (!$publicId && $errors = $this->paymentRepo->getErrors(Input::all())) { - $url = $publicId ? 'payments/'.$publicId.'/edit' : 'payments/create'; - - return Redirect::to($url) - ->withErrors($errors) - ->withInput(); + if (Input::get('email_receipt')) { + $this->contactMailer->sendPaymentConfirmation($payment); + Session::flash('message', trans('texts.created_payment_emailed_client')); } else { - $payment = $this->paymentRepo->save($publicId, Input::all()); - - if ($publicId) { - Session::flash('message', trans('texts.updated_payment')); - - return Redirect::to('payments/'); - } else { - if (Input::get('email_receipt')) { - $this->contactMailer->sendPaymentConfirmation($payment); - Session::flash('message', trans('texts.created_payment_emailed_client')); - } else { - Session::flash('message', trans('texts.created_payment')); - } - - return Redirect::to('clients/'.Input::get('client')); - } + Session::flash('message', trans('texts.created_payment')); } + + return redirect()->to($payment->client->getRoute()); + } + + public function update(UpdatePaymentRequest $request) + { + $input = $request->input(); + $payment = $this->paymentRepo->save($input); + + Session::flash('message', trans('texts.updated_payment')); + + return redirect()->to($payment->getRoute()); } public function bulk() { $action = Input::get('action'); - $ids = Input::get('id') ? Input::get('id') : Input::get('ids'); - $count = $this->paymentRepo->bulk($ids, $action); + $ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids'); + $count = $this->paymentService->bulk($ids, $action); if ($count > 0) { $message = Utils::pluralize($action.'d_payment', $count); diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php index fdf9fa36e..a62b2f2d8 100644 --- a/app/Http/Controllers/PublicClientController.php +++ b/app/Http/Controllers/PublicClientController.php @@ -8,16 +8,18 @@ use Datatable; use App\Models\Invitation; use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\PaymentRepository; +use App\Ninja\Repositories\ActivityRepository; class PublicClientController extends BaseController { private $invoiceRepo; private $paymentRepo; - public function __construct(InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo) + public function __construct(InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo, ActivityRepository $activityRepo) { $this->invoiceRepo = $invoiceRepo; $this->paymentRepo = $paymentRepo; + $this->activityRepo = $activityRepo; } public function dashboard() @@ -47,15 +49,22 @@ class PublicClientController extends BaseController } $invoice = $invitation->invoice; - $query = DB::table('activities') - ->join('clients', 'clients.id', '=', 'activities.client_id') - ->where('activities.client_id', '=', $invoice->client_id) - ->where('activities.adjustment', '!=', 0) - ->select('activities.id', 'activities.message', 'activities.created_at', 'clients.currency_id', 'activities.balance', 'activities.adjustment'); + $query = $this->activityRepo->findByClientId($invoice->client_id); + $query->where('activities.adjustment', '!=', 0); return Datatable::query($query) ->addColumn('activities.id', function ($model) { return Utils::timestampToDateTimeString(strtotime($model->created_at)); }) - ->addColumn('message', function ($model) { return strip_tags(Utils::decodeActivity($model->message)); }) + ->addColumn('message', function ($model) { + $data = [ + 'client' => Utils::getClientDisplayName($model), + 'user' => $model->is_system ? ('' . trans('texts.system') . '') : ($model->user_first_name . ' ' . $model->user_last_name), + 'invoice' => trans('texts.invoice') . ' ' . $model->invoice, + 'contact' => Utils::getClientDisplayName($model), + 'payment' => trans('texts.payment') . ($model->payment ? ' ' . $model->payment : ''), + ]; + + return trans("texts.activity_{$model->activity_type_id}", $data); + }) ->addColumn('balance', function ($model) { return Utils::formatMoney($model->balance, $model->currency_id); }) ->addColumn('adjustment', function ($model) { return $model->adjustment != 0 ? Utils::wrapAdjustment($model->adjustment, $model->currency_id) : ''; }) ->make(); diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index f78a693d6..ccf1834a0 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -24,24 +24,24 @@ use App\Models\Invoice; use App\Ninja\Mailers\ContactMailer as Mailer; use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\ClientRepository; -use App\Ninja\Repositories\TaxRateRepository; -use App\Events\QuoteApproved; +use App\Events\QuoteInvitationWasApproved; +use App\Services\InvoiceService; class QuoteController extends BaseController { protected $mailer; protected $invoiceRepo; protected $clientRepo; - protected $taxRateRepo; + protected $invoiceService; - public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, TaxRateRepository $taxRateRepo) + public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService) { parent::__construct(); $this->mailer = $mailer; $this->invoiceRepo = $invoiceRepo; $this->clientRepo = $clientRepo; - $this->taxRateRepo = $taxRateRepo; + $this->invoiceService = $invoiceService; } public function index() @@ -87,7 +87,8 @@ class QuoteController extends BaseController $clientId = Client::getPrivateId($clientPublicId); } $invoice = $account->createInvoice(ENTITY_QUOTE, $clientId); - + $invoice->public_id = 0; + $data = [ 'entityType' => $invoice->getEntityType(), 'invoice' => $invoice, @@ -123,19 +124,19 @@ class QuoteController extends BaseController public function bulk() { - $action = Input::get('action'); + $action = Input::get('bulk_action') ?: Input::get('action');; if ($action == 'convert') { $invoice = Invoice::with('invoice_items')->scope(Input::get('id'))->firstOrFail(); - $clone = $this->invoiceRepo->cloneInvoice($invoice, $invoice->id); + $clone = $this->invoiceService->approveQuote($invoice); Session::flash('message', trans('texts.converted_to_invoice')); return Redirect::to('invoices/'.$clone->public_id); } - + $statusId = Input::get('statusId'); - $ids = Input::get('id') ? Input::get('id') : Input::get('ids'); - $count = $this->invoiceRepo->bulk($ids, $action, $statusId); + $ids = Input::get('bulk_public_id') ?: (Input::get('public_id') ?: Input::get('ids')); + $count = $this->invoiceService->bulk($ids, $action, $statusId); if ($count > 0) { $key = $action == 'mark' ? "updated_quote" : "{$action}d_quote"; @@ -155,19 +156,8 @@ class QuoteController extends BaseController $invitation = Invitation::with('invoice.invoice_items', 'invoice.invitations')->where('invitation_key', '=', $invitationKey)->firstOrFail(); $invoice = $invitation->invoice; - if ($invoice->is_quote && !$invoice->quote_invoice_id) { - Event::fire(new QuoteApproved($invoice)); - Activity::approveQuote($invitation); - - $invoice = $this->invoiceRepo->cloneInvoice($invoice, $invoice->id); - Session::flash('message', trans('texts.converted_to_invoice')); - - foreach ($invoice->invitations as $invitationClone) { - if ($invitation->contact_id == $invitationClone->contact_id) { - $invitationKey = $invitationClone->invitation_key; - } - } - } + $invitationKey = $this->invoiceService->approveQuote($invoice, $invitation); + Session::flash('message', trans('texts.converted_to_invoice')); return Redirect::to("view/{$invitationKey}"); } diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 0fec382e7..b357f8d82 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -156,7 +156,7 @@ class TaskController extends BaseController */ public function edit($publicId) { - $task = Task::scope($publicId)->with('client', 'invoice')->firstOrFail(); + $task = Task::scope($publicId)->with('client', 'invoice')->withTrashed()->firstOrFail(); $actions = []; if ($task->invoice) { @@ -240,7 +240,7 @@ class TaskController extends BaseController public function bulk() { $action = Input::get('action'); - $ids = Input::get('id') ? Input::get('id') : Input::get('ids'); + $ids = Input::get('public_id') ?: (Input::get('id') ?: Input::get('ids')); if ($action == 'stop') { $this->taskRepo->save($ids, ['action' => $action]); diff --git a/app/Http/Requests/CreateClientRequest.php b/app/Http/Requests/CreateClientRequest.php new file mode 100644 index 000000000..2d1716cc1 --- /dev/null +++ b/app/Http/Requests/CreateClientRequest.php @@ -0,0 +1,44 @@ + 'valid_contacts', + ]; + } + + public function validator($factory) + { + // support submiting the form with a single client record + $input = $this->input(); + if (isset($input['contact'])) { + $input['contacts'] = [$input['contact']]; + unset($input['contact']); + $this->replace($input); + } + + return $factory->make( + $this->input(), $this->container->call([$this, 'rules']), $this->messages() + ); + } +} diff --git a/app/Http/Requests/CreateCreditRequest.php b/app/Http/Requests/CreateCreditRequest.php new file mode 100644 index 000000000..f2dc44d31 --- /dev/null +++ b/app/Http/Requests/CreateCreditRequest.php @@ -0,0 +1,30 @@ + 'required', + 'amount' => 'required|positive', + ]; + } +} diff --git a/app/Http/Requests/CreatePaymentRequest.php b/app/Http/Requests/CreatePaymentRequest.php new file mode 100644 index 000000000..d0c814687 --- /dev/null +++ b/app/Http/Requests/CreatePaymentRequest.php @@ -0,0 +1,44 @@ +input(); + $rules = array( + 'client' => 'required', + 'invoice' => 'required', + 'amount' => 'required', + ); + + if ($input['payment_type_id'] == PAYMENT_TYPE_CREDIT) { + $rules['payment_type_id'] = 'has_credit:'.$input['client'].','.$input['amount']; + } + + if (isset($input['invoice']) && $input['invoice']) { + $invoice = Invoice::scope($input['invoice'])->firstOrFail(); + $rules['amount'] .= "|less_than:{$invoice->balance}"; + } + + return $rules; + } +} diff --git a/app/Http/Requests/SaveInvoiceRequest.php b/app/Http/Requests/SaveInvoiceRequest.php new file mode 100644 index 000000000..1e18c6b5f --- /dev/null +++ b/app/Http/Requests/SaveInvoiceRequest.php @@ -0,0 +1,44 @@ + 'valid_contacts', + 'invoice_number' => 'required|unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.Auth::user()->account_id, + 'discount' => 'positive', + ]; + + /* There's a problem parsing the dates + if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { + $rules['end_date'] = 'after' . Request::get('start_date'); + } + */ + + return $rules; + } +} diff --git a/app/Http/Requests/UpdateClientRequest.php b/app/Http/Requests/UpdateClientRequest.php new file mode 100644 index 000000000..b73e019c4 --- /dev/null +++ b/app/Http/Requests/UpdateClientRequest.php @@ -0,0 +1,29 @@ + 'valid_contacts', + ]; + } +} diff --git a/app/Http/Requests/UpdatePaymentRequest.php b/app/Http/Requests/UpdatePaymentRequest.php new file mode 100644 index 000000000..83b192280 --- /dev/null +++ b/app/Http/Requests/UpdatePaymentRequest.php @@ -0,0 +1,28 @@ +getDisplayName() : 'System'; - $entity = $entity ? $entity->getActivityKey() : ''; - $otherPerson = $otherPerson ? 'to '.$otherPerson->getDisplayName() : ''; - $token = Session::get('token_id') ? ' ('.trans('texts.token').')' : ''; - - return trim("$person $token $action $entity $otherPerson"); - } - - public static function decodeActivity($message) - { - $pattern = '/\[([\w]*):([\d]*):(.*)\]/i'; - preg_match($pattern, $message, $matches); - - if (count($matches) > 0) { - $match = $matches[0]; - $type = $matches[1]; - $publicId = $matches[2]; - $name = $matches[3]; - - $link = link_to($type.'s/'.$publicId, $name); - $message = str_replace($match, "$type $link", $message); + if ($firstName || $lastName) { + return $firstName.' '.$lastName; + } elseif ($email) { + return $email; + } else { + return trans('texts.guest'); } - - return $message; } public static function generateLicense() @@ -598,7 +581,7 @@ class Utils foreach ($data as $key => $val) { if (is_array($val)) { if ($key == 'account' || isset($mapped[$key])) { - unset($data[$key]); + // do nothing } else { $mapped[$key] = true; $data[$key] = Utils::hideIds($val, $mapped); @@ -795,4 +778,30 @@ class Utils $adjustment = Utils::formatMoney($adjustment, $currencyId); return "

$adjustment

"; } + + public static function copyContext($entity1, $entity2) + { + if (!$entity2) { + return $entity1; + } + + $fields = [ + 'contact_id', + 'payment_id', + 'invoice_id', + 'credit_id', + 'invitation_id' + ]; + + $fields1 = $entity1->getAttributes(); + $fields2 = $entity2->getAttributes(); + + foreach ($fields as $field) { + if (isset($fields2[$field]) && $fields2[$field]) { + $entity1->$field = $entity2->$field; + } + } + + return $entity1; + } } diff --git a/app/Listeners/ActivityListener.php b/app/Listeners/ActivityListener.php new file mode 100644 index 000000000..fe966d474 --- /dev/null +++ b/app/Listeners/ActivityListener.php @@ -0,0 +1,337 @@ +activityRepo = $activityRepo; + } + + // Clients + public function createdClient(ClientWasCreated $event) + { + $this->activityRepo->create( + $event->client, + ACTIVITY_TYPE_CREATE_CLIENT + ); + } + + public function deletedClient(ClientWasDeleted $event) + { + $this->activityRepo->create( + $event->client, + ACTIVITY_TYPE_DELETE_CLIENT + ); + } + + public function archivedClient(ClientWasArchived $event) + { + if ($event->client->is_deleted) { + return; + } + + $this->activityRepo->create( + $event->client, + ACTIVITY_TYPE_ARCHIVE_CLIENT + ); + } + + public function restoredClient(ClientWasRestored $event) + { + $this->activityRepo->create( + $event->client, + ACTIVITY_TYPE_RESTORE_CLIENT + ); + } + + + // Invoices + public function createdInvoice(InvoiceWasCreated $event) + { + $this->activityRepo->create( + $event->invoice, + ACTIVITY_TYPE_CREATE_INVOICE, + $event->invoice->getAdjustment() + ); + } + + public function updatedInvoice(InvoiceWasUpdated $event) + { + if ( ! $event->invoice->isChanged()) { + return; + } + + $backupInvoice = Invoice::with('invoice_items', 'client.account', 'client.contacts')->find($event->invoice->id); + + $activity = $this->activityRepo->create( + $event->invoice, + ACTIVITY_TYPE_UPDATE_INVOICE, + $event->invoice->getAdjustment() + ); + + $activity->json_backup = $backupInvoice->hidePrivateFields()->toJSON(); + $activity->save(); + } + + public function deletedInvoice(InvoiceWasDeleted $event) + { + $this->activityRepo->create( + $event->invoice, + ACTIVITY_TYPE_DELETE_INVOICE, + $event->invoice->balance * -1, + $event->invoice->getAmountPaid() * -1 + ); + } + + public function archivedInvoice(InvoiceWasArchived $event) + { + if ($event->invoice->is_deleted) { + return; + } + + $this->activityRepo->create( + $event->invoice, + ACTIVITY_TYPE_ARCHIVE_INVOICE + ); + } + + public function restoredInvoice(InvoiceWasRestored $event) + { + $this->activityRepo->create( + $event->invoice, + ACTIVITY_TYPE_RESTORE_INVOICE + ); + } + + public function emailedInvoice(InvoiceInvitationWasEmailed $event) + { + $this->activityRepo->create( + $event->invoice, + ACTIVITY_TYPE_EMAIL_INVOICE, + false, + false, + $event->invitation + ); + } + + public function viewedInvoice(InvoiceInvitationWasViewed $event) + { + $this->activityRepo->create( + $event->invoice, + ACTIVITY_TYPE_VIEW_INVOICE, + false, + false, + $event->invitation + ); + } + + + // Quotes + public function createdQuote(QuoteWasCreated $event) + { + $this->activityRepo->create( + $event->quote, + ACTIVITY_TYPE_CREATE_QUOTE + ); + } + + public function updatedQuote(QuoteWasUpdated $event) + { + if ( ! $event->quote->isChanged()) { + return; + } + + $backupQuote = Invoice::with('invoice_items', 'client.account', 'client.contacts')->find($event->quote->id); + + $activity = $this->activityRepo->create( + $event->quote, + ACTIVITY_TYPE_UPDATE_QUOTE, + $event->quote->getAdjustment() + ); + + $activity->json_backup = $backupQuote->hidePrivateFields()->toJSON(); + $activity->save(); + } + + public function deletedQuote(QuoteWasDeleted $event) + { + $this->activityRepo->create( + $event->quote, + ACTIVITY_TYPE_DELETE_QUOTE + ); + } + + public function archivedQuote(QuoteWasArchived $event) + { + if ($event->quote->is_deleted) { + return; + } + + $this->activityRepo->create( + $event->quote, + ACTIVITY_TYPE_ARCHIVE_QUOTE + ); + } + + public function restoredQuote(QuoteWasRestored $event) + { + $this->activityRepo->create( + $event->quote, + ACTIVITY_TYPE_RESTORE_QUOTE + ); + } + + public function emailedQuote(QuoteInvitationWasEmailed $event) + { + $this->activityRepo->create( + $event->quote, + ACTIVITY_TYPE_EMAIL_QUOTE, + false, + false, + $event->invitation + ); + } + + public function viewedQuote(QuoteInvitationWasViewed $event) + { + $this->activityRepo->create( + $event->quote, + ACTIVITY_TYPE_VIEW_QUOTE, + false, + false, + $event->invitation + ); + } + + public function approvedQuote(QuoteInvitationWasApproved $event) + { + $this->activityRepo->create( + $event->quote, + ACTIVITY_TYPE_APPROVE_QUOTE, + false, + false, + $event->invitation + ); + } + + + // Credits + public function createdCredit(CreditWasCreated $event) + { + $this->activityRepo->create( + $event->credit, + ACTIVITY_TYPE_CREATE_CREDIT + ); + } + + public function deletedCredit(CreditWasDeleted $event) + { + $this->activityRepo->create( + $event->credit, + ACTIVITY_TYPE_DELETE_CREDIT + ); + } + + public function archivedCredit(CreditWasArchived $event) + { + if ($event->credit->is_deleted) { + return; + } + + $this->activityRepo->create( + $event->credit, + ACTIVITY_TYPE_ARCHIVE_CREDIT + ); + } + + public function restoredCredit(CreditWasRestored $event) + { + $this->activityRepo->create( + $event->credit, + ACTIVITY_TYPE_RESTORE_CREDIT + ); + } + + + // Payments + public function createdPayment(PaymentWasCreated $event) + { + $this->activityRepo->create( + $event->payment, + ACTIVITY_TYPE_CREATE_PAYMENT, + $event->payment->amount * -1, + $event->payment->amount + ); + } + + public function deletedPayment(PaymentWasDeleted $event) + { + $this->activityRepo->create( + $event->payment, + ACTIVITY_TYPE_DELETE_PAYMENT, + $event->payment->amount, + $event->payment->amount * -1 + ); + } + + public function archivedPayment(PaymentWasArchived $event) + { + if ($event->payment->is_deleted) { + return; + } + + $this->activityRepo->create( + $event->payment, + ACTIVITY_TYPE_ARCHIVE_PAYMENT + ); + } + + public function restoredPayment(PaymentWasRestored $event) + { + $this->activityRepo->create( + $event->payment, + ACTIVITY_TYPE_RESTORE_PAYMENT + ); + } + +} diff --git a/app/Listeners/CreditListener.php b/app/Listeners/CreditListener.php new file mode 100644 index 000000000..bed71a47f --- /dev/null +++ b/app/Listeners/CreditListener.php @@ -0,0 +1,33 @@ +creditRepo = $creditRepo; + } + + public function deletedPayment(PaymentWasDeleted $event) + { + $payment = $event->payment; + + // if the payment was from a credit we need to refund the credit + if ($payment->payment_type_id != PAYMENT_TYPE_CREDIT) { + return; + } + + $credit = Credit::createNew(); + $credit->client_id = $payment->client_id; + $credit->credit_date = Carbon::now()->toDateTimeString(); + $credit->balance = $credit->amount = $payment->amount; + $credit->private_notes = $payment->transaction_reference; + $credit->save(); + } +} diff --git a/app/Listeners/HandleInvoicePaid.php b/app/Listeners/HandleInvoicePaid.php deleted file mode 100644 index d072abd00..000000000 --- a/app/Listeners/HandleInvoicePaid.php +++ /dev/null @@ -1,48 +0,0 @@ -userMailer = $userMailer; - $this->contactMailer = $contactMailer; - } - - /** - * Handle the event. - * - * @param InvoicePaid $event - * @return void - */ - public function handle(InvoicePaid $event) - { - $payment = $event->payment; - $invoice = $payment->invoice; - - $this->contactMailer->sendPaymentConfirmation($payment); - - foreach ($invoice->account->users as $user) - { - if ($user->{'notify_paid'}) - { - $this->userMailer->sendNotification($user, $invoice, 'paid', $payment); - } - } - } - -} diff --git a/app/Listeners/HandleInvoiceSent.php b/app/Listeners/HandleInvoiceSent.php deleted file mode 100644 index 119936e95..000000000 --- a/app/Listeners/HandleInvoiceSent.php +++ /dev/null @@ -1,42 +0,0 @@ -userMailer = $userMailer; - } - - /** - * Handle the event. - * - * @param InvoiceSent $event - * @return void - */ - public function handle(InvoiceSent $event) - { - $invoice = $event->invoice; - - foreach ($invoice->account->users as $user) - { - if ($user->{'notify_sent'}) - { - $this->userMailer->sendNotification($user, $invoice, 'sent'); - } - } - } - -} diff --git a/app/Listeners/HandleInvoiceViewed.php b/app/Listeners/HandleInvoiceViewed.php deleted file mode 100644 index 47ee62a85..000000000 --- a/app/Listeners/HandleInvoiceViewed.php +++ /dev/null @@ -1,42 +0,0 @@ -userMailer = $userMailer; - } - - /** - * Handle the event. - * - * @param InvoiceViewed $event - * @return void - */ - public function handle(InvoiceViewed $event) - { - $invoice = $event->invoice; - - foreach ($invoice->account->users as $user) - { - if ($user->{'notify_viewed'}) - { - $this->userMailer->sendNotification($user, $invoice, 'viewed'); - } - } - } - -} diff --git a/app/Listeners/HandleQuoteApproved.php b/app/Listeners/HandleQuoteApproved.php deleted file mode 100644 index 3a49aa9b5..000000000 --- a/app/Listeners/HandleQuoteApproved.php +++ /dev/null @@ -1,42 +0,0 @@ -userMailer = $userMailer; - } - - /** - * Handle the event. - * - * @param QuoteApproved $event - * @return void - */ - public function handle(QuoteApproved $event) - { - $invoice = $event->invoice; - - foreach ($invoice->account->users as $user) - { - if ($user->{'notify_approved'}) - { - $this->userMailer->sendNotification($user, $invoice, 'approved'); - } - } - } - -} diff --git a/app/Listeners/HandleUserSignedUp.php b/app/Listeners/HandleUserSignedUp.php index 5bc4eab28..08961e161 100644 --- a/app/Listeners/HandleUserSignedUp.php +++ b/app/Listeners/HandleUserSignedUp.php @@ -41,12 +41,6 @@ class HandleUserSignedUp $this->accountRepo->registerNinjaUser($user); } - $activities = Activity::scope()->get(); - foreach ($activities as $activity) { - $activity->message = str_replace('Guest', $user->getFullName(), $activity->message); - $activity->save(); - } - session([SESSION_COUNTER => -1]); } } diff --git a/app/Listeners/InvoiceListener.php b/app/Listeners/InvoiceListener.php new file mode 100644 index 000000000..35762d8f8 --- /dev/null +++ b/app/Listeners/InvoiceListener.php @@ -0,0 +1,49 @@ +payment; + $invoice = $payment->invoice; + $adjustment = $payment->amount * -1; + $partial = max(0, $invoice->partial - $payment->amount); + + $invoice->updateBalances($adjustment, $partial); + $invoice->updatePaidStatus(); + } + + public function updatedInvoice(InvoiceWasUpdated $event) + { + $invoice = $event->invoice; + $invoice->updatePaidStatus(); + } + + public function emailedInvoice(InvoiceWasEmailed $event) + { + $invoice = $event->invoice; + $invoice->markSent(); + } + + public function viewedInvoice(InvoiceInvitationWasViewed $event) + { + $invitation = $event->invitation; + $invitation->markViewed(); + } + + public function deletedPayment(PaymentWasDeleted $event) + { + $payment = $event->payment; + $invoice = $payment->invoice; + $adjustment = $payment->amount; + + $invoice->updateBalances($adjustment); + $invoice->updatePaidStatus(); + } +} diff --git a/app/Listeners/NotificationListener.php b/app/Listeners/NotificationListener.php new file mode 100644 index 000000000..aba304457 --- /dev/null +++ b/app/Listeners/NotificationListener.php @@ -0,0 +1,71 @@ +userMailer = $userMailer; + $this->contactMailer = $contactMailer; + } + + private function sendEmails($invoice, $type, $payment = null) + { + foreach ($invoice->account->users as $user) + { + if ($user->{"notify_{$type}"}) + { + $this->userMailer->sendNotification($user, $invoice, $type, $payment); + } + } + } + + public function emailedInvoice(InvoiceWasEmailed $event) + { + $this->sendEmails($event->invoice, 'sent'); + } + + public function emailedQuote(QuoteWasEmailed $event) + { + $this->sendEmails($event->quote, 'sent'); + } + + public function viewedInvoice(InvoiceInvitationWasViewed $event) + { + $this->sendEmails($event->invoice, 'viewed'); + } + + public function viewedQuote(QuoteInvitationWasViewed $event) + { + $this->sendEmails($event->quote, 'viewed'); + } + + public function approvedQuote(QuoteInvitationWasApproved $event) + { + $this->sendEmails($event->quote, 'approved'); + } + + public function createdPayment(PaymentWasCreated $event) + { + // only send emails for online payments + if ( ! $event->payment->account_gateway_id) { + return; + } + + $this->contactMailer->sendPaymentConfirmation($event->payment); + $this->sendEmails($event->payment->invoice, 'paid', $event->payment); + } + +} \ No newline at end of file diff --git a/app/Listeners/QuoteListener.php b/app/Listeners/QuoteListener.php new file mode 100644 index 000000000..a228dc110 --- /dev/null +++ b/app/Listeners/QuoteListener.php @@ -0,0 +1,19 @@ +quote; + $quote->markSent(); + } + + public function viewedQuote(QuoteInvitationWasViewed $event) + { + $invitation = $event->invitation; + $invitation->markViewed(); + } +} diff --git a/app/Listeners/SubscriptionListener.php b/app/Listeners/SubscriptionListener.php new file mode 100644 index 000000000..7ef7a1116 --- /dev/null +++ b/app/Listeners/SubscriptionListener.php @@ -0,0 +1,47 @@ +checkSubscriptions(ACTIVITY_TYPE_CREATE_CLIENT, $event->client); + } + + public function createdQuote(QuoteWasCreated $event) + { + $this->checkSubscriptions(ACTIVITY_TYPE_CREATE_QUOTE, $event->quote); + } + + public function createdPayment(PaymentWasCreated $event) + { + $this->checkSubscriptions(ACTIVITY_TYPE_CREATE_PAYMENT, $event->payment); + } + + public function createdCredit(CreditWasCreated $event) + { + $this->checkSubscriptions(ACTIVITY_TYPE_CREATE_CREDIT, $event->credit); + } + + public function createdInvoice(InvoiceWasCreated $event) + { + $this->checkSubscriptions(ACTIVITY_TYPE_CREATE_INVOICE, $event->invoice); + } + + private function checkSubscriptions($activityTypeId, $entity) + { + $subscription = $entity->account->getSubscription($activityTypeId); + + if ($subscription) { + Utils::notifyZapier($subscription, $entity); + } + } +} diff --git a/app/Listeners/TaskListener.php b/app/Listeners/TaskListener.php new file mode 100644 index 000000000..b52c2fd5f --- /dev/null +++ b/app/Listeners/TaskListener.php @@ -0,0 +1,14 @@ +invoice->id) + ->update(['invoice_id' => null]); + } +} diff --git a/app/Models/Account.php b/app/Models/Account.php index c768f0bc6..c1637700a 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -251,7 +251,7 @@ class Account extends Eloquent $invoice->start_date = Utils::today(); $invoice->invoice_design_id = $this->invoice_design_id; $invoice->client_id = $clientId; - + if ($entityType === ENTITY_RECURRING_INVOICE) { $invoice->invoice_number = microtime(true); $invoice->is_recurring = true; @@ -316,7 +316,7 @@ class Account extends Eloquent $pattern = str_replace($search, $replace, $pattern); - if ($invoice->client->id) { + if ($invoice->client_id) { $pattern = $this->getClientInvoiceNumber($pattern, $invoice); } @@ -330,13 +330,11 @@ class Account extends Eloquent } $search = [ - //'{$clientId}', '{$custom1}', '{$custom2}', ]; $replace = [ - //str_pad($client->public_id, 3, '0', STR_PAD_LEFT), $invoice->client->custom_value1, $invoice->client->custom_value2, ]; @@ -344,17 +342,6 @@ class Account extends Eloquent return str_replace($search, $replace, $pattern); } - // if we're using a pattern we don't know the next number until a client - // is selected, to support this the default value is blank - public function getDefaultInvoiceNumber($invoice = false) - { - if ($this->hasClientNumberPattern($invoice)) { - return false; - } - - return $this->getNextInvoiceNumber($invoice); - } - public function getCounter($isQuote) { return $isQuote && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter; @@ -372,7 +359,7 @@ class Account extends Eloquent // confirm the invoice number isn't already taken do { - $number = $prefix.str_pad($counter, 4, "0", STR_PAD_LEFT); + $number = $prefix.str_pad($counter, 4, '0', STR_PAD_LEFT); $check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first(); $counter++; $counterOffset++; diff --git a/app/Models/Activity.php b/app/Models/Activity.php index edb20a7c9..fdf41ce54 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -23,480 +23,60 @@ class Activity extends Eloquent public function user() { - return $this->belongsTo('App\Models\User'); + return $this->belongsTo('App\Models\User')->withTrashed(); } - private static function getBlank($entity = false) + public function contact() { - $activity = new Activity(); - - if ($entity) { - $activity->user_id = $entity instanceof User ? $entity->id : $entity->user_id; - $activity->account_id = $entity->account_id; - } elseif (Auth::check()) { - $activity->user_id = Auth::user()->id; - $activity->account_id = Auth::user()->account_id; - } else { - Utils::fatalError(); - } - - $activity->token_id = Session::get('token_id', null); - $activity->ip = Request::getClientIp(); - - return $activity; + return $this->belongsTo('App\Models\Contact')->withTrashed(); } - public static function createClient($client, $notify = true) + public function client() { - $activity = Activity::getBlank(); - $activity->client_id = $client->id; - $activity->activity_type_id = ACTIVITY_TYPE_CREATE_CLIENT; - $activity->message = Utils::encodeActivity(Auth::user(), 'created', $client); - $activity->save(); - - if ($notify) { - Activity::checkSubscriptions(EVENT_CREATE_CLIENT, $client); - } + return $this->belongsTo('App\Models\Client')->withTrashed(); } - public static function updateClient($client) + public function invoice() { - if ($client->isBeingDeleted()) { - $activity = Activity::getBlank(); - $activity->client_id = $client->id; - $activity->activity_type_id = ACTIVITY_TYPE_DELETE_CLIENT; - $activity->message = Utils::encodeActivity(Auth::user(), 'deleted', $client); - $activity->balance = $client->balance; - $activity->save(); - } + return $this->belongsTo('App\Models\Invoice')->withTrashed(); } - public static function archiveClient($client) + public function credit() { - if (!$client->is_deleted) { - $activity = Activity::getBlank(); - $activity->client_id = $client->id; - $activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_CLIENT; - $activity->message = Utils::encodeActivity(Auth::user(), 'archived', $client); - $activity->balance = $client->balance; - $activity->save(); - } + return $this->belongsTo('App\Models\Credit')->withTrashed(); } - public static function restoreClient($client) + public function payment() { - $activity = Activity::getBlank(); - $activity->client_id = $client->id; - $activity->activity_type_id = ACTIVITY_TYPE_RESTORE_CLIENT; - $activity->message = Utils::encodeActivity(Auth::user(), 'restored', $client); - $activity->balance = $client->balance; - $activity->save(); + return $this->belongsTo('App\Models\Payment')->withTrashed(); } - public static function createInvoice($invoice) + public static function calcMessage($activityTypeId, $client, $user, $invoice, $contactId, $payment, $credit, $isSystem) { - if (Auth::check()) { - $message = Utils::encodeActivity(Auth::user(), 'created', $invoice); - } else { - $message = Utils::encodeActivity(null, 'created', $invoice); - } + $data = [ + 'client' => link_to($client->getRoute(), $client->getDisplayName()), + 'user' => $isSystem ? '' . trans('texts.system') . '' : $user->getDisplayName(), + 'invoice' => $invoice ? link_to($invoice->getRoute(), $invoice->getDisplayName()) : null, + 'quote' => $invoice ? link_to($invoice->getRoute(), $invoice->getDisplayName()) : null, + 'contact' => $contactId ? $client->getDisplayName() : $user->getDisplayName(), + 'payment' => $payment ? $payment->transaction_reference : null, + 'credit' => $credit ? Utils::formatMoney($credit->amount, $client->currency_id) : null, + ]; - $adjustment = 0; - $client = $invoice->client; - if (!$invoice->is_quote && !$invoice->is_recurring) { - $adjustment = $invoice->amount; - $client->balance = $client->balance + $adjustment; - $client->save(); - } - - $activity = Activity::getBlank($invoice); - $activity->invoice_id = $invoice->id; - $activity->client_id = $invoice->client_id; - $activity->activity_type_id = $invoice->is_quote ? ACTIVITY_TYPE_CREATE_QUOTE : ACTIVITY_TYPE_CREATE_INVOICE; - $activity->message = $message; - $activity->balance = $client->balance; - $activity->adjustment = $adjustment; - $activity->save(); - - Activity::checkSubscriptions($invoice->is_quote ? EVENT_CREATE_QUOTE : EVENT_CREATE_INVOICE, $invoice); + return trans("texts.activity_{$activityTypeId}", $data); } - public static function archiveInvoice($invoice) + public function getMessage() { - if (!$invoice->is_deleted) { - $activity = Activity::getBlank(); - $activity->invoice_id = $invoice->id; - $activity->client_id = $invoice->client_id; - $activity->activity_type_id = $invoice->is_quote ? ACTIVITY_TYPE_ARCHIVE_QUOTE : ACTIVITY_TYPE_ARCHIVE_INVOICE; - $activity->message = Utils::encodeActivity(Auth::user(), 'archived', $invoice); - $activity->balance = $invoice->client->balance; - - $activity->save(); - } - } - - public static function restoreInvoice($invoice) - { - $activity = Activity::getBlank(); - $activity->invoice_id = $invoice->id; - $activity->client_id = $invoice->client_id; - $activity->activity_type_id = $invoice->is_quote ? ACTIVITY_TYPE_RESTORE_QUOTE : ACTIVITY_TYPE_RESTORE_INVOICE; - $activity->message = Utils::encodeActivity(Auth::user(), 'restored', $invoice); - $activity->balance = $invoice->client->balance; - - $activity->save(); - } - - public static function emailInvoice($invitation) - { - $invoice = $invitation->invoice; - $client = $invoice->client; - - if (!$invoice->isSent()) { - $invoice->invoice_status_id = INVOICE_STATUS_SENT; - $invoice->save(); - } - - $activity = Activity::getBlank($invitation); - $activity->client_id = $invitation->invoice->client_id; - $activity->invoice_id = $invitation->invoice_id; - $activity->contact_id = $invitation->contact_id; - $activity->activity_type_id = $invitation->invoice ? ACTIVITY_TYPE_EMAIL_QUOTE : ACTIVITY_TYPE_EMAIL_INVOICE; - $activity->message = Utils::encodeActivity(Auth::check() ? Auth::user() : null, 'emailed', $invitation->invoice, $invitation->contact); - $activity->balance = $client->balance; - $activity->save(); - } - - public static function updateInvoice($invoice) - { - $client = $invoice->client; - - if ($invoice->isBeingDeleted()) { - $adjustment = 0; - if (!$invoice->is_quote && !$invoice->is_recurring) { - $adjustment = $invoice->balance * -1; - $client->balance = $client->balance - $invoice->balance; - $client->paid_to_date = $client->paid_to_date - ($invoice->amount - $invoice->balance); - $client->save(); - } - - $activity = Activity::getBlank(); - $activity->client_id = $invoice->client_id; - $activity->invoice_id = $invoice->id; - $activity->activity_type_id = $invoice->is_quote ? ACTIVITY_TYPE_DELETE_QUOTE : ACTIVITY_TYPE_DELETE_INVOICE; - $activity->message = Utils::encodeActivity(Auth::user(), 'deleted', $invoice); - $activity->balance = $invoice->client->balance; - $activity->adjustment = $adjustment; - $activity->save(); - - // Release any tasks associated with the deleted invoice - Task::where('invoice_id', '=', $invoice->id) - ->update(['invoice_id' => null]); - } else { - $diff = floatval($invoice->amount) - floatval($invoice->getOriginal('amount')); - - $fieldChanged = false; - foreach (['invoice_number', 'po_number', 'invoice_date', 'due_date', 'terms', 'public_notes', 'invoice_footer', 'partial'] as $field) { - if ($invoice->$field != $invoice->getOriginal($field)) { - $fieldChanged = true; - break; - } - } - - if ($diff != 0 || $fieldChanged) { - $backupInvoice = Invoice::with('invoice_items', 'client.account', 'client.contacts')->find($invoice->id); - - if ($diff != 0 && !$invoice->is_quote && !$invoice->is_recurring) { - $client->balance = $client->balance + $diff; - $client->save(); - } - - $activity = Activity::getBlank($invoice); - $activity->client_id = $invoice->client_id; - $activity->invoice_id = $invoice->id; - $activity->activity_type_id = $invoice->is_quote ? ACTIVITY_TYPE_UPDATE_QUOTE : ACTIVITY_TYPE_UPDATE_INVOICE; - $activity->message = Utils::encodeActivity(Auth::user(), 'updated', $invoice); - $activity->balance = $client->balance; - $activity->adjustment = $invoice->is_quote || $invoice->is_recurring ? 0 : $diff; - $activity->json_backup = $backupInvoice->hidePrivateFields()->toJSON(); - $activity->save(); - - if ($invoice->isPaid() && $invoice->balance > 0) { - $invoice->invoice_status_id = INVOICE_STATUS_PARTIAL; - } elseif ($invoice->invoice_status_id && $invoice->balance == 0) { - $invoice->invoice_status_id = INVOICE_STATUS_PAID; - } - } - } - } - - public static function viewInvoice($invitation) - { - if (Session::get($invitation->invitation_key)) { - return; - } - - Session::put($invitation->invitation_key, true); - $invoice = $invitation->invoice; - - if (!$invoice->isViewed()) { - $invoice->invoice_status_id = INVOICE_STATUS_VIEWED; - $invoice->save(); - } - - $now = Carbon::now()->toDateTimeString(); - - $invitation->viewed_date = $now; - $invitation->save(); - - $client = $invoice->client; - $client->last_login = $now; - $client->save(); - - $activity = Activity::getBlank($invitation); - $activity->client_id = $invitation->invoice->client_id; - $activity->invitation_id = $invitation->id; - $activity->contact_id = $invitation->contact_id; - $activity->invoice_id = $invitation->invoice_id; - $activity->activity_type_id = $invitation->invoice->is_quote ? ACTIVITY_TYPE_VIEW_QUOTE : ACTIVITY_TYPE_VIEW_INVOICE; - $activity->message = Utils::encodeActivity($invitation->contact, 'viewed', $invitation->invoice); - $activity->balance = $invitation->invoice->client->balance; - $activity->save(); - } - - public static function approveQuote($invitation) { - - $activity = Activity::getBlank($invitation); - $activity->client_id = $invitation->invoice->client_id; - $activity->invitation_id = $invitation->id; - $activity->contact_id = $invitation->contact_id; - $activity->invoice_id = $invitation->invoice_id; - $activity->activity_type_id = ACTIVITY_TYPE_APPROVE_QUOTE; - $activity->message = Utils::encodeActivity($invitation->contact, 'approved', $invitation->invoice); - $activity->balance = $invitation->invoice->client->balance; - $activity->save(); - } - - public static function createPayment($payment) - { - $client = $payment->client; - $client->balance = $client->balance - $payment->amount; - $client->paid_to_date = $client->paid_to_date + $payment->amount; - $client->save(); - - if ($payment->contact_id) { - $activity = Activity::getBlank($client); - $activity->contact_id = $payment->contact_id; - $activity->message = Utils::encodeActivity($payment->invitation->contact, 'entered '.$payment->getName().' for ', $payment->invoice); - } else { - $activity = Activity::getBlank($client); - $message = $payment->payment_type_id == PAYMENT_TYPE_CREDIT ? 'applied credit for ' : 'entered '.$payment->getName().' for '; - $activity->message = Utils::encodeActivity(Auth::user(), $message, $payment->invoice); - } - - $activity->payment_id = $payment->id; - - if ($payment->invoice_id) { - $activity->invoice_id = $payment->invoice_id; - - $invoice = $payment->invoice; - $invoice->balance = $invoice->balance - $payment->amount; - $invoice->invoice_status_id = ($invoice->balance > 0) ? INVOICE_STATUS_PARTIAL : INVOICE_STATUS_PAID; - if ($invoice->partial > 0) { - $invoice->partial = max(0, $invoice->partial - $payment->amount); - } - $invoice->save(); - } - - $activity->payment_id = $payment->id; - $activity->client_id = $payment->client_id; - $activity->activity_type_id = ACTIVITY_TYPE_CREATE_PAYMENT; - $activity->balance = $client->balance; - $activity->adjustment = $payment->amount * -1; - $activity->save(); - - Activity::checkSubscriptions(EVENT_CREATE_PAYMENT, $payment); - } - - public static function updatePayment($payment) - { - if ($payment->isBeingDeleted()) { - $client = $payment->client; - $client->balance = $client->balance + $payment->amount; - $client->paid_to_date = $client->paid_to_date - $payment->amount; - $client->save(); - - $invoice = $payment->invoice; - $invoice->balance = $invoice->balance + $payment->amount; - if ($invoice->isPaid() && $invoice->balance > 0) { - $invoice->invoice_status_id = ($invoice->balance == $invoice->amount ? INVOICE_STATUS_DRAFT : INVOICE_STATUS_PARTIAL); - } - $invoice->save(); - - // deleting a payment from credit creates a new credit - if ($payment->payment_type_id == PAYMENT_TYPE_CREDIT) { - $credit = Credit::createNew(); - $credit->client_id = $client->id; - $credit->credit_date = Carbon::now()->toDateTimeString(); - $credit->balance = $credit->amount = $payment->amount; - $credit->private_notes = $payment->transaction_reference; - $credit->save(); - } - - $activity = Activity::getBlank(); - $activity->payment_id = $payment->id; - $activity->client_id = $invoice->client_id; - $activity->invoice_id = $invoice->id; - $activity->activity_type_id = ACTIVITY_TYPE_DELETE_PAYMENT; - $activity->message = Utils::encodeActivity(Auth::user(), 'deleted '.$payment->getName()); - $activity->balance = $client->balance; - $activity->adjustment = $payment->amount; - $activity->save(); - } else { - /* - $diff = floatval($invoice->amount) - floatval($invoice->getOriginal('amount')); - - if ($diff == 0) - { - return; - } - - $client = $invoice->client; - $client->balance = $client->balance + $diff; - $client->save(); - - $activity = Activity::getBlank($invoice); - $activity->client_id = $invoice->client_id; - $activity->invoice_id = $invoice->id; - $activity->activity_type_id = ACTIVITY_TYPE_UPDATE_INVOICE; - $activity->message = Utils::encodeActivity(Auth::user(), 'updated', $invoice); - $activity->balance = $client->balance; - $activity->adjustment = $diff; - $activity->json_backup = $backupInvoice->hidePrivateFields()->toJSON(); - $activity->save(); - */ - } - } - - public static function archivePayment($payment) - { - if ($payment->is_deleted) { - return; - } - - $client = $payment->client; - $invoice = $payment->invoice; - - $activity = Activity::getBlank(); - $activity->payment_id = $payment->id; - $activity->invoice_id = $invoice->id; - $activity->client_id = $client->id; - $activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_PAYMENT; - $activity->message = Utils::encodeActivity(Auth::user(), 'archived '.$payment->getName()); - $activity->balance = $client->balance; - $activity->adjustment = 0; - $activity->save(); - } - - public static function restorePayment($payment) - { - $client = $payment->client; - $invoice = $payment->invoice; - - $activity = Activity::getBlank(); - $activity->payment_id = $payment->id; - $activity->invoice_id = $invoice->id; - $activity->client_id = $client->id; - $activity->activity_type_id = ACTIVITY_TYPE_RESTORE_PAYMENT; - $activity->message = Utils::encodeActivity(Auth::user(), 'restored '.$payment->getName()); - $activity->balance = $client->balance; - $activity->adjustment = 0; - $activity->save(); - } - - public static function createCredit($credit) - { - $activity = Activity::getBlank(); - $activity->message = Utils::encodeActivity(Auth::user(), 'entered '.Utils::formatMoney($credit->amount, $credit->client->getCurrencyId()).' credit'); - $activity->credit_id = $credit->id; - $activity->client_id = $credit->client_id; - $activity->activity_type_id = ACTIVITY_TYPE_CREATE_CREDIT; - $activity->balance = $credit->client->balance; - $activity->save(); - } - - public static function updateCredit($credit) - { - if ($credit->isBeingDeleted()) { - $activity = Activity::getBlank(); - $activity->credit_id = $credit->id; - $activity->client_id = $credit->client_id; - $activity->activity_type_id = ACTIVITY_TYPE_DELETE_CREDIT; - $activity->message = Utils::encodeActivity(Auth::user(), 'deleted '.Utils::formatMoney($credit->balance, $credit->client->getCurrencyId()).' credit'); - $activity->balance = $credit->client->balance; - $activity->save(); - } else { - /* - $diff = floatval($invoice->amount) - floatval($invoice->getOriginal('amount')); - - if ($diff == 0) - { - return; - } - - $client = $invoice->client; - $client->balance = $client->balance + $diff; - $client->save(); - - $activity = Activity::getBlank($invoice); - $activity->client_id = $invoice->client_id; - $activity->invoice_id = $invoice->id; - $activity->activity_type_id = ACTIVITY_TYPE_UPDATE_INVOICE; - $activity->message = Utils::encodeActivity(Auth::user(), 'updated', $invoice); - $activity->balance = $client->balance; - $activity->adjustment = $diff; - $activity->json_backup = $backupInvoice->hidePrivateFields()->toJSON(); - $activity->save(); - */ - } - } - - public static function archiveCredit($credit) - { - if ($credit->is_deleted) { - return; - } - - $activity = Activity::getBlank(); - $activity->client_id = $credit->client_id; - $activity->credit_id = $credit->id; - $activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_CREDIT; - $activity->message = Utils::encodeActivity(Auth::user(), 'archived '.Utils::formatMoney($credit->balance, $credit->client->getCurrencyId()).' credit'); - $activity->balance = $credit->client->balance; - $activity->save(); - } - - public static function restoreCredit($credit) - { - $activity = Activity::getBlank(); - $activity->client_id = $credit->client_id; - $activity->credit_id = $credit->id; - $activity->activity_type_id = ACTIVITY_TYPE_RESTORE_CREDIT; - $activity->message = Utils::encodeActivity(Auth::user(), 'restored '.Utils::formatMoney($credit->balance, $credit->client->getCurrencyId()).' credit'); - $activity->balance = $credit->client->balance; - $activity->save(); - } - - private static function checkSubscriptions($event, $data) - { - if (!Auth::check()) { - return; - } - - $subscription = Auth::user()->account->getSubscription($event); - - if ($subscription) { - Utils::notifyZapier($subscription, $data); - } + return static::calcMessage( + $this->activity_type_id, + $this->client, + $this->user, + $this->invoice, + $this->contact_id, + $this->payment, + $this->credit, + $this->is_system + ); } } diff --git a/app/Models/BalanceAffecting.php b/app/Models/BalanceAffecting.php new file mode 100644 index 000000000..0ba99f73a --- /dev/null +++ b/app/Models/BalanceAffecting.php @@ -0,0 +1,6 @@ +belongsTo('App\Models\Industry'); } + public function addContact($data, $isPrimary = false) + { + $publicId = isset($data['public_id']) ? $data['public_id'] : false; + + if ($publicId && $publicId != '-1') { + $contact = Contact::scope($publicId)->firstOrFail(); + } else { + $contact = Contact::createNew(); + $contact->send_invoice = true; + } + + $contact->fill($data); + $contact->is_primary = $isPrimary; + + return $this->contacts()->save($contact); + } + + public function updateBalances($balanceAdjustment, $paidToDateAdjustment) + { + if ($balanceAdjustment === 0 && $paidToDateAdjustment === 0) { + return; + } + + $this->balance = $this->balance + $balanceAdjustment; + $this->paid_to_date = $this->paid_to_date + $paidToDateAdjustment; + + $this->save(); + } + + public function getRoute() + { + return "/clients/{$this->public_id}"; + } + public function getTotalCredit() { return DB::table('credits') @@ -89,8 +147,11 @@ class Client extends EntityModel return $this->name; } - $contact = $this->contacts()->first(); + if ( ! count($this->contacts)) { + return ''; + } + $contact = $this->contacts[0]; return $contact->getDisplayName(); } @@ -178,24 +239,26 @@ class Client extends EntityModel { return $isQuote ? $this->quote_number_counter : $this->invoice_number_counter; } + + public function markLoggedIn() + { + $this->last_login = Carbon::now()->toDateTimeString(); + $this->save(); + } } -/* -Client::created(function($client) -{ - Activity::createClient($client); +Client::creating(function ($client) { + $client->setNullValues(); +}); + +Client::created(function ($client) { + event(new ClientWasCreated($client)); }); -*/ Client::updating(function ($client) { - Activity::updateClient($client); + $client->setNullValues(); }); -Client::deleting(function ($client) { - Activity::archiveClient($client); +Client::updated(function ($client) { + event(new ClientWasUpdated($client)); }); - -/*Client::restoring(function ($client) { - Activity::restoreClient($client); -}); -*/ \ No newline at end of file diff --git a/app/Models/Contact.php b/app/Models/Contact.php index 0856a6d43..731900e35 100644 --- a/app/Models/Contact.php +++ b/app/Models/Contact.php @@ -9,6 +9,14 @@ class Contact extends EntityModel use SoftDeletes; protected $dates = ['deleted_at']; + protected $fillable = [ + 'first_name', + 'last_name', + 'email', + 'phone', + 'send_invoice', + ]; + public static $fieldFirstName = 'Contact - First Name'; public static $fieldLastName = 'Contact - Last Name'; public static $fieldEmail = 'Contact - Email'; diff --git a/app/Models/Credit.php b/app/Models/Credit.php index 9a507bc6b..a89e9e925 100644 --- a/app/Models/Credit.php +++ b/app/Models/Credit.php @@ -1,12 +1,19 @@ belongsTo('App\Models\Account'); + } + public function invoice() { return $this->belongsTo('App\Models\Invoice')->withTrashed(); @@ -43,18 +50,10 @@ class Credit extends EntityModel } } +Credit::creating(function ($credit) { + +}); + Credit::created(function ($credit) { - Activity::createCredit($credit); -}); - -Credit::updating(function ($credit) { - Activity::updateCredit($credit); -}); - -Credit::deleting(function ($credit) { - Activity::archiveCredit($credit); -}); - -Credit::restoring(function ($credit) { - Activity::restoreCredit($credit); -}); + event(new CreditWasCreated($credit)); +}); \ No newline at end of file diff --git a/app/Models/EntityModel.php b/app/Models/EntityModel.php index f9a375dc4..9dd294cac 100644 --- a/app/Models/EntityModel.php +++ b/app/Models/EntityModel.php @@ -39,7 +39,7 @@ class EntityModel extends Eloquent { $className = get_called_class(); - return $className::scope($publicId)->pluck('id'); + return $className::scope($publicId)->withTrashed()->pluck('id'); } public function getActivityKey() @@ -112,8 +112,21 @@ class EntityModel extends Eloquent return $data; } - public function isBeingDeleted() + public function setNullValues() { - return $this->is_deleted && !$this->getOriginal('is_deleted'); + foreach ($this->fillable as $field) { + if (strstr($field, '_id') && !$this->$field) { + $this->$field = null; + } + } + } + + // converts "App\Models\Client" to "client_id" + public function getKeyField() + { + $class = get_class($this); + $parts = explode('\\', $class); + $name = $parts[count($parts)-1]; + return strtolower($name) . '_id'; } } diff --git a/app/Models/Invitation.php b/app/Models/Invitation.php index 00807b1a6..821b8b865 100644 --- a/app/Models/Invitation.php +++ b/app/Models/Invitation.php @@ -1,6 +1,7 @@ invitation_key; } + public function markViewed() + { + $invoice = $this->invoice; + $client = $invoice->client; + + $this->viewed_date = Carbon::now()->toDateTimeString(); + $this->save(); + + $invoice->markViewed(); + $client->markLoggedIn(); + } } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 7a0a8c953..59ea9a60d 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -2,9 +2,15 @@ use Utils; use DateTime; +use App\Models\BalanceAffecting; use Illuminate\Database\Eloquent\SoftDeletes; -class Invoice extends EntityModel +use App\Events\QuoteWasCreated; +use App\Events\QuoteWasUpdated; +use App\Events\InvoiceWasCreated; +use App\Events\InvoiceWasUpdated; + +class Invoice extends EntityModel implements BalanceAffecting { use SoftDeletes { SoftDeletes::trashed as parentTrashed; @@ -26,6 +32,64 @@ class Invoice extends EntityModel 'year', 'date:', ]; + + public function getRoute() + { + $entityType = $this->getEntityType(); + return "/{$entityType}s/{$this->public_id}/edit"; + } + + public function getDisplayName() + { + return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number; + } + + public function getAdjustment() + { + if ($this->is_quote || $this->is_recurring) { + return 0; + } + + return $this->getRawAdjustment(); + } + + private function getRawAdjustment() + { + return floatval($this->amount) - floatval($this->getOriginal('amount')); + } + + public function isChanged() + { + if ($this->getRawAdjustment() != 0) { + return true; + } + + foreach ([ + 'invoice_number', + 'po_number', + 'invoice_date', + 'due_date', + 'terms', + 'public_notes', + 'invoice_footer', + 'partial' + ] as $field) { + if ($this->$field != $this->getOriginal($field)) { + return true; + } + } + + return false; + } + + public function getAmountPaid() + { + if ($this->is_quote || $this->is_recurring) { + return 0; + } + + return ($this->amount - $this->balance); + } public function trashed() { @@ -81,6 +145,44 @@ class Invoice extends EntityModel return $this->hasMany('App\Models\Invitation')->orderBy('invitations.contact_id'); } + public function markSent() + { + if (!$this->isSent()) { + $this->invoice_status_id = INVOICE_STATUS_SENT; + $this->save(); + } + } + + public function markViewed() + { + if (!$this->isViewed()) { + $this->invoice_status_id = INVOICE_STATUS_VIEWED; + $this->save(); + } + } + + public function updatePaidStatus() + { + if ($this->isPaid() && $this->balance > 0) { + $this->invoice_status_id = ($this->balance == $this->amount ? INVOICE_STATUS_SENT : INVOICE_STATUS_PARTIAL); + $this->save(); + } elseif ($this->invoice_status_id && $this->amount && $this->balance == 0 && $this->invoice_status_id != INVOICE_STATUS_PAID) { + $this->invoice_status_id = INVOICE_STATUS_PAID; + $this->save(); + } + } + + public function updateBalances($balanceAdjustment, $partial = 0) + { + $this->balance = $this->balance + $balanceAdjustment; + + if ($this->partial > 0) { + $this->partial = $partial; + } + + $this->save(); + } + public function getName() { return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number; @@ -458,17 +560,17 @@ Invoice::creating(function ($invoice) { }); Invoice::created(function ($invoice) { - Activity::createInvoice($invoice); + if ($invoice->is_quote) { + event(new QuoteWasCreated($invoice)); + } else { + event(new InvoiceWasCreated($invoice)); + } }); Invoice::updating(function ($invoice) { - Activity::updateInvoice($invoice); -}); - -Invoice::deleting(function ($invoice) { - Activity::archiveInvoice($invoice); -}); - -Invoice::restoring(function ($invoice) { - Activity::restoreInvoice($invoice); + if ($invoice->is_quote) { + event(new QuoteWasUpdated($invoice)); + } else { + event(new InvoiceWasUpdated($invoice)); + } }); \ No newline at end of file diff --git a/app/Models/Payment.php b/app/Models/Payment.php index ee382dece..d0a1ae466 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -1,6 +1,7 @@ belongsTo('App\Models\Contact'); } + public function getRoute() + { + return "/payments/{$this->public_id}/edit"; + } + public function getAmount() { return Utils::formatMoney($this->amount, $this->client->getCurrencyId()); @@ -53,18 +59,10 @@ class Payment extends EntityModel } } +Payment::creating(function ($payment) { + +}); + Payment::created(function ($payment) { - Activity::createPayment($payment); -}); - -Payment::updating(function ($payment) { - Activity::updatePayment($payment); -}); - -Payment::deleting(function ($payment) { - Activity::archivePayment($payment); -}); - -Payment::restoring(function ($payment) { - Activity::restorePayment($payment); -}); + event(new PaymentWasCreated($payment)); +}); \ No newline at end of file diff --git a/app/Models/Task.php b/app/Models/Task.php index 4ccbf9688..fbdd00c46 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -82,20 +82,4 @@ class Task extends EntityModel { return round($this->getDuration() / (60 * 60), 2); } -} - -Task::created(function ($task) { - //Activity::createTask($task); -}); - -Task::updating(function ($task) { - //Activity::updateTask($task); -}); - -Task::deleting(function ($task) { - //Activity::archiveTask($task); -}); - -Task::restoring(function ($task) { - //Activity::restoreTask($task); -}); +} \ No newline at end of file diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php index ff4f28c51..d6d5e70a1 100644 --- a/app/Ninja/Mailers/ContactMailer.php +++ b/app/Ninja/Mailers/ContactMailer.php @@ -9,7 +9,12 @@ use App\Models\Invoice; use App\Models\Payment; use App\Models\Activity; use App\Models\Gateway; -use App\Events\InvoiceSent; + +use App\Events\InvoiceWasEmailed; +use App\Events\InvoiceInvitationWasEmailed; + +use App\Events\QuoteWasEmailed; +use App\Events\QuoteInvitationWasEmailed; class ContactMailer extends Mailer { @@ -44,7 +49,11 @@ class ContactMailer extends Mailer $account->loadLocalizationSettings(); if ($sent === true) { - Event::fire(new InvoiceSent($invoice)); + if ($invoice->is_quote) { + event(new QuoteWasEmailed($invoice)); + } else { + event(new InvoiceWasEmailed($invoice)); + } } return $sent ?: trans('texts.email_error'); @@ -97,7 +106,12 @@ class ContactMailer extends Mailer $response = $this->sendTo($invitation->contact->email, $fromEmail, $account->getDisplayName(), $subject, ENTITY_INVOICE, $data); if ($response === true) { - Activity::emailInvoice($invitation); + if ($invoice->is_quote) { + event(new QuoteInvitationWasEmailed($invoice, $invitation)); + } else { + event(new InvoiceInvitationWasEmailed($invoice, $invitation)); + } + return true; } else { return false; diff --git a/app/Ninja/Repositories/ActivityRepository.php b/app/Ninja/Repositories/ActivityRepository.php new file mode 100644 index 000000000..8b0692741 --- /dev/null +++ b/app/Ninja/Repositories/ActivityRepository.php @@ -0,0 +1,102 @@ +invoice->client; + } else { + $client = $entity->client; + } + + // init activity and copy over context + $activity = self::getBlank($altEntity ?: $client); + $activity = Utils::copyContext($activity, $entity); + $activity = Utils::copyContext($activity, $altEntity); + + $activity->client_id = $client->id; + $activity->activity_type_id = $activityTypeId; + $activity->adjustment = $balanceChange; + $activity->balance = $client->balance + $balanceChange; + + $keyField = $entity->getKeyField(); + $activity->$keyField = $entity->id; + + $activity->ip = Request::getClientIp(); + $activity->save(); + + $client->updateBalances($balanceChange, $paidToDateChange); + + return $activity; + } + + private function getBlank($entity) + { + $activity = new Activity(); + + if (Auth::check()) { + $activity->user_id = Auth::user()->id; + $activity->account_id = Auth::user()->account_id; + } else { + $activity->user_id = $entity->user_id; + $activity->account_id = $entity->account_id; + + if ( ! $entity instanceof Invitation) { + $activity->is_system = true; + } + } + + $activity->token_id = session('token_id'); + + return $activity; + } + + public function findByClientId($clientId) + { + return DB::table('activities') + ->join('users', 'users.id', '=', 'activities.user_id') + ->join('clients', 'clients.id', '=', 'activities.client_id') + ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id') + ->leftJoin('invoices', 'invoices.id', '=', 'activities.invoice_id') + ->leftJoin('payments', 'payments.id', '=', 'activities.payment_id') + ->leftJoin('credits', 'credits.id', '=', 'activities.credit_id') + ->where('activities.client_id', '=', $clientId) + ->where('contacts.is_primary', '=', 1) + ->whereNull('contacts.deleted_at') + ->select( + 'activities.id', + 'activities.created_at', + 'activities.contact_id', + 'activities.activity_type_id', + 'activities.is_system', + 'clients.currency_id', + 'activities.balance', + 'activities.adjustment', + 'users.first_name as user_first_name', + 'users.last_name as user_last_name', + 'users.email as user_email', + 'invoices.invoice_number as invoice', + 'invoices.public_id as invoice_public_id', + 'invoices.is_recurring', + 'clients.currency_id', + 'clients.name as client_name', + 'clients.public_id as client_public_id', + 'contacts.id as contact', + 'contacts.first_name as first_name', + 'contacts.last_name as last_name', + 'contacts.email as email', + 'payments.transaction_reference as payment', + 'credits.amount as credit'); + } + +} \ No newline at end of file diff --git a/app/Ninja/Repositories/BaseRepository.php b/app/Ninja/Repositories/BaseRepository.php new file mode 100644 index 000000000..b9e9d0459 --- /dev/null +++ b/app/Ninja/Repositories/BaseRepository.php @@ -0,0 +1,58 @@ +getClassName(); + return new $className(); + } + + private function dispatchEvent($entity, $type) + { + $className = 'App\Events\\' . ucfirst($entity->getEntityType()) . 'Was' . $type; + event(new $className($entity)); + } + + public function archive($entity) + { + $entity->delete(); + + $this->dispatchEvent($entity, 'Archived'); + } + + public function restore($entity) + { + $entity->restore(); + + $entity->is_deleted = false; + $entity->save(); + + $this->dispatchEvent($entity, 'Restored'); + } + + public function delete($entity) + { + $entity->is_deleted = true; + $entity->save(); + + $entity->delete(); + + $this->dispatchEvent($entity, 'Deleted'); + } + + public function findByPublicIds($ids) + { + return $this->getInstance()->scope($ids)->get(); + } + + public function findByPublicIdsWithTrashed($ids) + { + return $this->getInstance()->scope($ids)->withTrashed()->get(); + } +} diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index 091c66f9c..728b0bcca 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -1,11 +1,17 @@ 'email|required_without:first_name', - 'first_name' => 'required_without:email', - ]); - if ($validator->fails()) { - return $validator->messages(); - } + $publicId = isset($data['public_id']) ? $data['public_id'] : false; - return false; - } - - public function save($publicId, $data, $notify = true) - { - if (!$publicId || $publicId == "-1") { + if (!$publicId || $publicId == '-1') { $client = Client::createNew(); - $contact = Contact::createNew(); - $contact->is_primary = true; - $contact->send_invoice = true; } else { $client = Client::scope($publicId)->with('contacts')->firstOrFail(); - $contact = $client->contacts()->where('is_primary', '=', true)->firstOrFail(); - } - - if (isset($data['name'])) { - $client->name = trim($data['name']); - } - if (isset($data['id_number'])) { - $client->id_number = trim($data['id_number']); - } - if (isset($data['vat_number'])) { - $client->vat_number = trim($data['vat_number']); - } - if (isset($data['work_phone'])) { - $client->work_phone = trim($data['work_phone']); - } - if (isset($data['custom_value1'])) { - $client->custom_value1 = trim($data['custom_value1']); - } - if (isset($data['custom_value2'])) { - $client->custom_value2 = trim($data['custom_value2']); - } - if (isset($data['address1'])) { - $client->address1 = trim($data['address1']); - } - if (isset($data['address2'])) { - $client->address2 = trim($data['address2']); - } - if (isset($data['city'])) { - $client->city = trim($data['city']); - } - if (isset($data['state'])) { - $client->state = trim($data['state']); - } - if (isset($data['postal_code'])) { - $client->postal_code = trim($data['postal_code']); - } - if (isset($data['country_id'])) { - $client->country_id = $data['country_id'] ? $data['country_id'] : null; - } - if (isset($data['private_notes'])) { - $client->private_notes = trim($data['private_notes']); - } - if (isset($data['size_id'])) { - $client->size_id = $data['size_id'] ? $data['size_id'] : null; - } - if (isset($data['industry_id'])) { - $client->industry_id = $data['industry_id'] ? $data['industry_id'] : null; - } - if (isset($data['currency_id'])) { - $client->currency_id = $data['currency_id'] ? $data['currency_id'] : null; - } - if (isset($data['language_id'])) { - $client->language_id = $data['language_id'] ? $data['language_id'] : null; - } - if (isset($data['payment_terms'])) { - $client->payment_terms = $data['payment_terms']; - } - if (isset($data['website'])) { - $client->website = trim($data['website']); } + $client->fill($data); $client->save(); - $isPrimary = true; + $first = true; + $contacts = isset($data['contact']) ? [$data['contact']] : $data['contacts']; $contactIds = []; - if (isset($data['contact'])) { - $info = $data['contact']; - if (isset($info['email'])) { - $contact->email = trim($info['email']); - } - if (isset($info['first_name'])) { - $contact->first_name = trim($info['first_name']); - } - if (isset($info['last_name'])) { - $contact->last_name = trim($info['last_name']); - } - if (isset($info['phone'])) { - $contact->phone = trim($info['phone']); - } - $contact->is_primary = true; - $contact->send_invoice = true; - $client->contacts()->save($contact); - } else { - foreach ($data['contacts'] as $record) { - $record = (array) $record; - - if ($publicId != "-1" && isset($record['public_id']) && $record['public_id']) { - $contact = Contact::scope($record['public_id'])->firstOrFail(); - } else { - $contact = Contact::createNew(); - } - - if (isset($record['email'])) { - $contact->email = trim($record['email']); - } - if (isset($record['first_name'])) { - $contact->first_name = trim($record['first_name']); - } - if (isset($record['last_name'])) { - $contact->last_name = trim($record['last_name']); - } - if (isset($record['phone'])) { - $contact->phone = trim($record['phone']); - } - $contact->is_primary = $isPrimary; - $contact->send_invoice = isset($record['send_invoice']) ? $record['send_invoice'] : true; - $isPrimary = false; - - $client->contacts()->save($contact); - $contactIds[] = $contact->public_id; - } - - foreach ($client->contacts as $contact) { - if (!in_array($contact->public_id, $contactIds)) { - $contact->delete(); - } - } + foreach ($data['contacts'] as $contact) { + $contact = $client->addContact($contact, $first); + $contactIds[] = $contact->public_id; + $first = false; } - $client->save(); - - if (!$publicId || $publicId == "-1") { - Activity::createClient($client, $notify); + foreach ($client->contacts as $contact) { + if (!in_array($contact->public_id, $contactIds)) { + $contact->delete(); + } } return $client; } - - public function bulk($ids, $action) - { - $clients = Client::withTrashed()->scope($ids)->get(); - - foreach ($clients as $client) { - if ($action == 'restore') { - $client->restore(); - - $client->is_deleted = false; - $client->save(); - } else { - if ($action == 'delete') { - $client->is_deleted = true; - $client->save(); - } - - $client->delete(); - } - } - - return count($clients); - } } diff --git a/app/Ninja/Repositories/CreditRepository.php b/app/Ninja/Repositories/CreditRepository.php index 700467620..1ac543c5b 100644 --- a/app/Ninja/Repositories/CreditRepository.php +++ b/app/Ninja/Repositories/CreditRepository.php @@ -1,11 +1,17 @@ firstOrFail(); } else { @@ -50,28 +58,4 @@ class CreditRepository return $credit; } - - public function bulk($ids, $action) - { - if (!$ids) { - return 0; - } - - $credits = Credit::withTrashed()->scope($ids)->get(); - - foreach ($credits as $credit) { - if ($action == 'restore') { - $credit->restore(); - } else { - if ($action == 'delete') { - $credit->is_deleted = true; - $credit->save(); - } - - $credit->delete(); - } - } - - return count($credits); - } } diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index 912310c76..0e9f9dadf 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -8,9 +8,15 @@ use App\Models\Invitation; use App\Models\Product; use App\Models\Task; use App\Services\PaymentService; +use App\Ninja\Repositories\BaseRepository; -class InvoiceRepository +class InvoiceRepository extends BaseRepository { + public function getClassName() + { + return 'App\Models\Invoice'; + } + public function __construct(PaymentService $paymentService) { $this->paymentService = $paymentService; @@ -214,51 +220,24 @@ class InvoiceRepository } return "

$label

"; } - - public function getErrors($input) - { - $contact = (array) $input->client->contacts[0]; - $rules = [ - 'email' => 'email|required_without:first_name', - 'first_name' => 'required_without:email', - ]; - $validator = \Validator::make($contact, $rules); - - if ($validator->fails()) { - return $validator; - } - - $invoice = (array) $input; - $invoiceId = isset($invoice['public_id']) && $invoice['public_id'] ? Invoice::getPrivateId($invoice['public_id']) : null; - $rules = [ - 'invoice_number' => 'required|unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.\Auth::user()->account_id, - 'discount' => 'positive', - ]; - - if ($invoice['is_recurring'] && $invoice['start_date'] && $invoice['end_date']) { - $rules['end_date'] = 'after:'.$invoice['start_date']; - } - - $validator = \Validator::make($invoice, $rules); - - if ($validator->fails()) { - return $validator; - } - - return false; - } - - public function save($publicId, $data, $entityType) + + public function save($data) { $account = \Auth::user()->account; + $publicId = isset($data['public_id']) ? $data['public_id'] : false; + + $isNew = !$publicId || $publicId == '-1'; - if ($publicId) { - $invoice = Invoice::scope($publicId)->firstOrFail(); - } else { - if ($data['is_recurring']) { + if ($isNew) { + $entityType = ENTITY_INVOICE; + if (isset($data['is_recurring']) && $data['is_recurring']) { $entityType = ENTITY_RECURRING_INVOICE; + } elseif (isset($data['is_quote']) && $data['is_quote']) { + $entityType = ENTITY_QUOTE; } $invoice = $account->createInvoice($entityType, $data['client_id']); + } else { + $invoice = Invoice::scope($publicId)->firstOrFail(); } if ((isset($data['set_default_terms']) && $data['set_default_terms']) @@ -280,7 +259,7 @@ class InvoiceRepository $invoice->is_amount_discount = $data['is_amount_discount'] ? true : false; $invoice->partial = round(Utils::parseFloat($data['partial']), 2); $invoice->invoice_date = isset($data['invoice_date_sql']) ? $data['invoice_date_sql'] : Utils::toSqlDate($data['invoice_date']); - $invoice->has_tasks = isset($data['has_tasks']) ? $data['has_tasks'] : false; + $invoice->has_tasks = isset($data['has_tasks']) ? ($data['has_tasks'] ? true : false) : false; if ($invoice->is_recurring) { if ($invoice->start_date && $invoice->start_date != Utils::toSqlDate($data['start_date'])) { @@ -362,12 +341,20 @@ class InvoiceRepository $total *= (100 - $invoice->discount) / 100; } } - - $invoice->custom_value1 = round($data['custom_value1'], 2); - $invoice->custom_value2 = round($data['custom_value2'], 2); - $invoice->custom_taxes1 = $data['custom_taxes1'] ? true : false; - $invoice->custom_taxes2 = $data['custom_taxes2'] ? true : false; - + + if (isset($data['custom_value1'])) { + $invoice->custom_value1 = round($data['custom_value1'], 2); + if ($isNew) { + $invoice->custom_taxes1 = $account->custom_invoice_taxes1 ?: false; + } + } + if (isset($data['custom_value2'])) { + $invoice->custom_value2 = round($data['custom_value2'], 2); + if ($isNew) { + $invoice->custom_taxes2 = $account->custom_invoice_taxes2 ?: false; + } + } + if (isset($data['custom_text_value1'])) { $invoice->custom_text_value1 = trim($data['custom_text_value1']); } @@ -543,31 +530,10 @@ class InvoiceRepository return $clone; } - public function bulk($ids, $action, $statusId = false) + public function mark($invoice, $statusId) { - if (!$ids) { - return 0; - } - - $invoices = Invoice::withTrashed()->scope($ids)->get(); - - foreach ($invoices as $invoice) { - if ($action == 'mark') { - $invoice->invoice_status_id = $statusId; - $invoice->save(); - } elseif ($action == 'restore') { - $invoice->restore(); - } else { - if ($action == 'delete') { - $invoice->is_deleted = true; - $invoice->save(); - } - - $invoice->delete(); - } - } - - return count($invoices); + $invoice->invoice_status_id = $statusId; + $invoice->save(); } public function findInvoiceByInvitation($invitationKey) @@ -637,10 +603,10 @@ class InvoiceRepository $invoice->tax_name = $recurInvoice->tax_name; $invoice->tax_rate = $recurInvoice->tax_rate; $invoice->invoice_design_id = $recurInvoice->invoice_design_id; - $invoice->custom_value1 = $recurInvoice->custom_value1; - $invoice->custom_value2 = $recurInvoice->custom_value2; - $invoice->custom_taxes1 = $recurInvoice->custom_taxes1; - $invoice->custom_taxes2 = $recurInvoice->custom_taxes2; + $invoice->custom_value1 = $recurInvoice->custom_value1 ?: 0; + $invoice->custom_value2 = $recurInvoice->custom_value2 ?: 0; + $invoice->custom_taxes1 = $recurInvoice->custom_taxes1 ?: 0; + $invoice->custom_taxes2 = $recurInvoice->custom_taxes2 ?: 0; $invoice->custom_text_value1 = $recurInvoice->custom_text_value1; $invoice->custom_text_value2 = $recurInvoice->custom_text_value2; $invoice->is_amount_discount = $recurInvoice->is_amount_discount; diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php index c8f69325c..67c6d9fe6 100644 --- a/app/Ninja/Repositories/PaymentRepository.php +++ b/app/Ninja/Repositories/PaymentRepository.php @@ -1,13 +1,19 @@ 'required', - 'invoice' => 'required', - 'amount' => 'required', - ); - - if ($input['payment_type_id'] == PAYMENT_TYPE_CREDIT) { - $rules['payment_type_id'] = 'has_credit:'.$input['client'].','.$input['amount']; - } - - if (isset($input['invoice']) && $input['invoice']) { - $invoice = Invoice::scope($input['invoice'])->firstOrFail(); - $rules['amount'] .= "|less_than:{$invoice->balance}"; - } - - $validator = \Validator::make($input, $rules); - - if ($validator->fails()) { - return $validator; - } - - return false; - } - - public function save($publicId = null, $input) + public function save($input) { + $publicId = isset($input['public_id']) ? $input['public_id'] : false; + if ($publicId) { $payment = Payment::scope($publicId)->firstOrFail(); } else { @@ -145,28 +127,4 @@ class PaymentRepository return $payment; } - - public function bulk($ids, $action) - { - if (!$ids) { - return 0; - } - - $payments = Payment::withTrashed()->scope($ids)->get(); - - foreach ($payments as $payment) { - if ($action == 'restore') { - $payment->restore(); - } else { - if ($action == 'delete') { - $payment->is_deleted = true; - $payment->save(); - } - - $payment->delete(); - } - } - - return count($payments); - } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 07e5f0313..efb642bfe 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -148,6 +148,19 @@ class AppServiceProvider extends ServiceProvider { Validator::extend('has_counter', function($attribute, $value, $parameters) { return !$value || strstr($value, '{$counter}'); }); + + Validator::extend('valid_contacts', function($attribute, $value, $parameters) { + foreach ($value as $contact) { + $validator = Validator::make($contact, [ + 'email' => 'email|required_without:first_name', + 'first_name' => 'required_without:email', + ]); + if ($validator->fails()) { + return false; + } + } + return true; + }); } /** diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 739c95868..3b7e80077 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -11,6 +11,123 @@ class EventServiceProvider extends ServiceProvider { * @var array */ protected $listen = [ + + // Clients + 'App\Events\ClientWasCreated' => [ + 'App\Listeners\ActivityListener@createdClient', + 'App\Listeners\SubscriptionListener@createdClient', + ], + 'App\Events\ClientWasArchived' => [ + 'App\Listeners\ActivityListener@archivedClient', + ], + 'App\Events\ClientWasDeleted' => [ + 'App\Listeners\ActivityListener@deletedClient', + ], + 'App\Events\ClientWasRestored' => [ + 'App\Listeners\ActivityListener@restoredClient', + ], + + // Invoices + 'App\Events\InvoiceWasCreated' => [ + 'App\Listeners\ActivityListener@createdInvoice', + 'App\Listeners\SubscriptionListener@createdInvoice', + ], + 'App\Events\InvoiceWasUpdated' => [ + 'App\Listeners\ActivityListener@updatedInvoice', + 'App\Listeners\InvoiceListener@updatedInvoice', + ], + 'App\Events\InvoiceWasArchived' => [ + 'App\Listeners\ActivityListener@archivedInvoice', + ], + 'App\Events\InvoiceWasDeleted' => [ + 'App\Listeners\ActivityListener@deletedInvoice', + 'App\Listeners\TaskListener@deletedInvoice', + ], + 'App\Events\InvoiceWasRestored' => [ + 'App\Listeners\ActivityListener@restoredInvoice', + ], + 'App\Events\InvoiceWasEmailed' => [ + 'App\Listeners\NotificationListener@emailedInvoice', + 'App\Listeners\InvoiceListener@emailedInvoice', + ], + 'App\Events\InvoiceInvitationWasEmailed' => [ + 'App\Listeners\ActivityListener@emailedInvoice', + ], + 'App\Events\InvoiceInvitationWasViewed' => [ + 'App\Listeners\ActivityListener@viewedInvoice', + 'App\Listeners\NotificationListener@viewedInvoice', + 'App\Listeners\InvoiceListener@viewedInvoice', + ], + + // Quotes + 'App\Events\QuoteWasCreated' => [ + 'App\Listeners\ActivityListener@createdQuote', + 'App\Listeners\SubscriptionListener@createdQuote', + ], + 'App\Events\QuoteWasUpdated' => [ + 'App\Listeners\ActivityListener@updatedQuote', + ], + 'App\Events\QuoteWasArchived' => [ + 'App\Listeners\ActivityListener@archivedQuote', + ], + 'App\Events\QuoteWasDeleted' => [ + 'App\Listeners\ActivityListener@deletedQuote', + ], + 'App\Events\QuoteWasRestored' => [ + 'App\Listeners\ActivityListener@restoredQuote', + ], + 'App\Events\QuoteWasEmailed' => [ + 'App\Listeners\QuoteListener@emailedQuote', + 'App\Listeners\NotificationListener@emailedQuote', + ], + 'App\Events\QuoteInvitationWasEmailed' => [ + 'App\Listeners\ActivityListener@emailedQuote', + ], + 'App\Events\QuoteInvitationWasViewed' => [ + 'App\Listeners\ActivityListener@viewedQuote', + 'App\Listeners\NotificationListener@viewedQuote', + 'App\Listeners\QuoteListener@viewedQuote', + ], + 'App\Events\QuoteInvitationWasApproved' => [ + 'App\Listeners\ActivityListener@approvedQuote', + 'App\Listeners\NotificationListener@approvedQuote', + ], + + // Payments + 'App\Events\PaymentWasCreated' => [ + 'App\Listeners\ActivityListener@createdPayment', + 'App\Listeners\SubscriptionListener@createdPayment', + 'App\Listeners\InvoiceListener@createdPayment', + 'App\Listeners\NotificationListener@createdPayment', + ], + 'App\Events\PaymentWasArchived' => [ + 'App\Listeners\ActivityListener@archivedPayment', + ], + 'App\Events\PaymentWasDeleted' => [ + 'App\Listeners\ActivityListener@deletedPayment', + 'App\Listeners\InvoiceListener@deletedPayment', + 'App\Listeners\CreditListener@deletedPayment', + ], + 'App\Events\PaymentWasRestored' => [ + 'App\Listeners\ActivityListener@restoredPayment', + ], + + // Credits + 'App\Events\CreditWasCreated' => [ + 'App\Listeners\ActivityListener@createdCredit', + 'App\Listeners\SubscriptionListener@createdCredit', + ], + 'App\Events\CreditWasArchived' => [ + 'App\Listeners\ActivityListener@archivedCredit', + ], + 'App\Events\CreditWasDeleted' => [ + 'App\Listeners\ActivityListener@deletedCredit', + ], + 'App\Events\CreditWasRestored' => [ + 'App\Listeners\ActivityListener@restoredCredit', + ], + + // User events 'App\Events\UserSignedUp' => [ 'App\Listeners\HandleUserSignedUp', ], @@ -20,18 +137,7 @@ class EventServiceProvider extends ServiceProvider { 'App\Events\UserSettingsChanged' => [ 'App\Listeners\HandleUserSettingsChanged', ], - 'App\Events\InvoiceSent' => [ - 'App\Listeners\HandleInvoiceSent', - ], - 'App\Events\InvoiceViewed' => [ - 'App\Listeners\HandleInvoiceViewed', - ], - 'App\Events\InvoicePaid' => [ - 'App\Listeners\HandleInvoicePaid', - ], - 'App\Events\QuoteApproved' => [ - 'App\Listeners\HandleQuoteApproved', - ], + ]; /** diff --git a/app/Services/BaseService.php b/app/Services/BaseService.php new file mode 100644 index 000000000..affb8a235 --- /dev/null +++ b/app/Services/BaseService.php @@ -0,0 +1,28 @@ +getRepo()->findByPublicIdsWithTrashed($ids); + + foreach ($entities as $entity) { + $this->getRepo()->$action($entity, $param); + } + + return count($entities); + } +} diff --git a/app/Services/ClientService.php b/app/Services/ClientService.php new file mode 100644 index 000000000..fea373d7c --- /dev/null +++ b/app/Services/ClientService.php @@ -0,0 +1,25 @@ +clientRepo = $clientRepo; + } + + protected function getRepo() + { + return $this->clientRepo; + } + + public function save($data) + { + return $this->clientRepo->save($data); + } +} \ No newline at end of file diff --git a/app/Services/CreditService.php b/app/Services/CreditService.php new file mode 100644 index 000000000..d1e23bfb4 --- /dev/null +++ b/app/Services/CreditService.php @@ -0,0 +1,25 @@ +creditRepo = $creditRepo; + } + + protected function getRepo() + { + return $this->creditRepo; + } + + public function save($data) + { + return $this->creditRepo->save($data); + } +} \ No newline at end of file diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php new file mode 100644 index 000000000..eaa60d999 --- /dev/null +++ b/app/Services/InvoiceService.php @@ -0,0 +1,81 @@ +clientRepo = $clientRepo; + $this->invoiceRepo = $invoiceRepo; + } + + protected function getRepo() + { + return $this->invoiceRepo; + } + + public function save($data) + { + if (isset($data['client'])) { + $client = $this->clientRepo->save($data['client']); + $data['client_id'] = $client->id; + } + + $invoice = $this->invoiceRepo->save($data); + + $client = $invoice->client; + $client->load('contacts'); + $sendInvoiceIds = []; + + foreach ($client->contacts as $contact) { + if ($contact->send_invoice || count($client->contacts) == 1) { + $sendInvoiceIds[] = $contact->id; + } + } + + foreach ($client->contacts as $contact) { + $invitation = Invitation::scope()->whereContactId($contact->id)->whereInvoiceId($invoice->id)->first(); + + if (in_array($contact->id, $sendInvoiceIds) && !$invitation) { + $invitation = Invitation::createNew(); + $invitation->invoice_id = $invoice->id; + $invitation->contact_id = $contact->id; + $invitation->invitation_key = str_random(RANDOM_KEY_LENGTH); + $invitation->save(); + } elseif (!in_array($contact->id, $sendInvoiceIds) && $invitation) { + $invitation->delete(); + } + } + + return $invoice; + } + + public function approveQuote($quote, $invitation = null) + { + if (!$quote->is_quote || $quote->quote_invoice_id) { + return null; + } + + $invoice = $this->invoiceRepo->cloneInvoice($quote, $quote->id); + + if (!$invitation) { + return $invoice; + } + + event(new QuoteInvitationWasApproved($invoice, $invitation)); + + foreach ($invoice->invitations as $invoiceInvitation) { + if ($invitation->contact_id == $invoiceInvitation->contact_id) { + return $invoiceInvitation->invitation_key; + } + } + } +} \ No newline at end of file diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index b8ad04237..e9d99fc7e 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -10,18 +10,26 @@ use App\Models\Payment; use App\Models\Account; use App\Models\Country; use App\Models\AccountGatewayToken; +use App\Ninja\Repositories\PaymentRepository; use App\Ninja\Repositories\AccountRepository; -use App\Events\InvoicePaid; - -class PaymentService { +use App\Services\BaseService; +use App\Events\PaymentWasCreated; +class PaymentService extends BaseService +{ public $lastError; - public function __construct(AccountRepository $accountRepo) + public function __construct(PaymentRepository $paymentRepo, AccountRepository $accountRepo) { + $this->paymentRepo = $paymentRepo; $this->accountRepo = $accountRepo; } + protected function getRepo() + { + return $this->paymentRepo; + } + public function createGateway($accountGateway) { $gateway = Omnipay::create($accountGateway->gateway->provider); @@ -208,8 +216,6 @@ class PaymentService { $payment->save(); - Event::fire(new InvoicePaid($payment)); - return $payment; } diff --git a/database/migrations/2015_10_27_180214_add_is_system_to_activities.php b/database/migrations/2015_10_27_180214_add_is_system_to_activities.php new file mode 100644 index 000000000..615ea88b4 --- /dev/null +++ b/database/migrations/2015_10_27_180214_add_is_system_to_activities.php @@ -0,0 +1,51 @@ +boolean('is_system')->default(0); + }); + + $activities = Activity::where('message', 'like', '%System%')->get(); + foreach ($activities as $activity) { + $activity->is_system = true; + $activity->save(); + } + + Schema::table('activities', function($table) + { + $table->dropColumn('message'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('activities', function($table) + { + $table->dropColumn('is_system'); + }); + + Schema::table('activities', function($table) + { + $table->text('message')->nullable(); + }); + } + +} diff --git a/resources/lang/da/texts.php b/resources/lang/da/texts.php index 71258403c..85ae4b1df 100644 --- a/resources/lang/da/texts.php +++ b/resources/lang/da/texts.php @@ -826,5 +826,62 @@ 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', + ); diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index bd0117be2..4a7945d9a 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -841,4 +841,48 @@ return array( 'default_tax_rate_id' => 'Standard Steuersatz', 'tax_rate' => 'Steuersatz', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', + ); diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 50bb943c1..1468594b8 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -852,4 +852,38 @@ return array( 'quote_counter' => 'Quote Counter', 'type' => 'Type', + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', + + ); diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index bd46a6106..3027ef372 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -74,6 +74,7 @@ return array( "notmasked" => "The values are masked", "less_than" => 'The :attribute must be less than :value', "has_counter" => 'The value must contain {$counter}', + "valid_contacts" => 'All of the contacts must have either an email or name', /* |-------------------------------------------------------------------------- diff --git a/resources/lang/es/texts.php b/resources/lang/es/texts.php index 4f350af3f..eaff3ef5a 100644 --- a/resources/lang/es/texts.php +++ b/resources/lang/es/texts.php @@ -804,6 +804,62 @@ return array( 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', ); diff --git a/resources/lang/es_ES/texts.php b/resources/lang/es_ES/texts.php index 95ef2b5ca..7c59cdc82 100644 --- a/resources/lang/es_ES/texts.php +++ b/resources/lang/es_ES/texts.php @@ -825,5 +825,61 @@ return array( 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', ); diff --git a/resources/lang/fr/texts.php b/resources/lang/fr/texts.php index e04f699fc..8651fb475 100644 --- a/resources/lang/fr/texts.php +++ b/resources/lang/fr/texts.php @@ -552,7 +552,7 @@ return array( 'pay_with_paypal' => 'PayPal', 'pay_with_card' => 'Carte bancaire', - 'change_password' => 'Changer de pot de passe', + 'change_password' => 'Changer le mot de passe', 'current_password' => 'Mot de passe actuel', 'new_password' => 'Nouveau mot de passe', 'confirm_password' => 'Confirmer le mot de passe', @@ -819,5 +819,61 @@ return array( 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', + ); diff --git a/resources/lang/fr_CA/texts.php b/resources/lang/fr_CA/texts.php index 1b0e7d822..50e4f43e2 100644 --- a/resources/lang/fr_CA/texts.php +++ b/resources/lang/fr_CA/texts.php @@ -819,6 +819,61 @@ return array( 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', ); diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index 916f78bc8..fece6b644 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -821,5 +821,61 @@ return array( 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', ); diff --git a/resources/lang/lt/texts.php b/resources/lang/lt/texts.php index 2c20fc364..9bd47ae4a 100644 --- a/resources/lang/lt/texts.php +++ b/resources/lang/lt/texts.php @@ -828,6 +828,62 @@ return array( 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', ); diff --git a/resources/lang/nb_NO/texts.php b/resources/lang/nb_NO/texts.php index 36ad7c3aa..16fd7c7bb 100644 --- a/resources/lang/nb_NO/texts.php +++ b/resources/lang/nb_NO/texts.php @@ -826,5 +826,61 @@ return array( 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', ); \ No newline at end of file diff --git a/resources/lang/nl/texts.php b/resources/lang/nl/texts.php index bacb5e9d3..2b8b55f98 100644 --- a/resources/lang/nl/texts.php +++ b/resources/lang/nl/texts.php @@ -821,5 +821,61 @@ return array( 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', ); diff --git a/resources/lang/pt_BR/texts.php b/resources/lang/pt_BR/texts.php index 0bbccacef..d524d7f16 100644 --- a/resources/lang/pt_BR/texts.php +++ b/resources/lang/pt_BR/texts.php @@ -820,5 +820,61 @@ return array( 'no_longer_running' => 'Esta fatura não está agendada', 'general_settings' => 'Configurações Gerais', 'customize' => 'Personalizar', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', ); diff --git a/resources/lang/sv/texts.php b/resources/lang/sv/texts.php index ed06fa1c8..e605fd758 100644 --- a/resources/lang/sv/texts.php +++ b/resources/lang/sv/texts.php @@ -105,12 +105,12 @@ return array( 'recurring_invoices' => 'Återkommande fakturor', 'recurring_help' => '

Skicka automatiskt fakturor till kund varje vecka, månad, kvartal eller årsvis.

Använd :MONTH, :QUARTER eller :YEAR för dynamiskt datum. Enkla formler fungerar också, t.ex. :MONTH-1

-

Exempel på dynamiska fakturavariabler::

- ', +

Exempel på dynamiska fakturavariabler::

+ ', // dashboard 'in_total_revenue' => 'i totala intäkter', @@ -283,19 +283,19 @@ return array( // Security alerts 'confide' => [ - 'too_many_attempts' => 'För många felaktiga försök. Pröva igen om ett par minuter.', - 'wrong_credentials' => 'Felaktig e-postadress eller lösenord.', - 'confirmation' => 'Ditt konto har bekräftats!', - 'wrong_confirmation' => 'Felaktig bekräftelsekod.', - 'password_forgot' => 'Information angående återställning av ditt lösenord har skickats till dig via e-post.', - 'password_reset' => 'Ditt lösenord har uppdaterats.', - 'wrong_password_reset' => 'Felaktigt lösenord. Försök igen', + 'too_many_attempts' => 'För många felaktiga försök. Pröva igen om ett par minuter.', + 'wrong_credentials' => 'Felaktig e-postadress eller lösenord.', + 'confirmation' => 'Ditt konto har bekräftats!', + 'wrong_confirmation' => 'Felaktig bekräftelsekod.', + 'password_forgot' => 'Information angående återställning av ditt lösenord har skickats till dig via e-post.', + 'password_reset' => 'Ditt lösenord har uppdaterats.', + 'wrong_password_reset' => 'Felaktigt lösenord. Försök igen', ], // Pro Plan 'pro_plan' => [ - 'remove_logo' => ':link för att ta bort Invoice Ninja loggan genom att uppgradera till Pro Plan', - 'remove_logo_link' => 'Klicka här', + 'remove_logo' => ':link för att ta bort Invoice Ninja loggan genom att uppgradera till Pro Plan', + 'remove_logo_link' => 'Klicka här', ], 'logout' => 'Logga ut', @@ -312,11 +312,11 @@ return array( 'pro_plan_product' => 'Pro Plan', 'pro_plan_description' => 'Ett års prenumeration på Invoice Ninja Pro.', 'pro_plan_success' => 'Tack för att du väljer Invoice Ninja\'s Pro!

 
- Nästa steg

En faktura har skickats till din angivna e-postadress. - Var vänlig och följ instruktionerna på fakturan för att betala för ett års - Pro fakturering och få tillgång till alla fantastiska Pro-funktioner.

- Hittar du inte fakturan? Behöver du support? Vi hjälper dig! - -- maila oss på contact@invoiceninja.com', + Nästa steg

En faktura har skickats till din angivna e-postadress. + Var vänlig och följ instruktionerna på fakturan för att betala för ett års + Pro fakturering och få tillgång till alla fantastiska Pro-funktioner.

+ Hittar du inte fakturan? Behöver du support? Vi hjälper dig! + -- maila oss på contact@invoiceninja.com', 'unsaved_changes' => 'Du har osparade ändringar', 'custom_fields' => 'Anpassade fält', @@ -748,9 +748,9 @@ return array( 'primary_user' => 'Primary User', 'help' => 'Help', 'customize_help' => '

We use pdfmake to define the invoice designs declaratively. The pdfmake playground provide\'s a great way to see the library in action.

-

You can access any invoice field by adding Value to the end. For example $invoiceNumberValue displays the invoice number.

-

To access a child property using dot notation. For example to show the client name you could use $client.nameValue.

-

If you need help figuring something out post a question to our support forum.

', +

You can access any invoice field by adding Value to the end. For example $invoiceNumberValue displays the invoice number.

+

To access a child property using dot notation. For example to show the client name you could use $client.nameValue.

+

If you need help figuring something out post a question to our support forum.

', 'invoice_due_date' => 'Due Date', 'quote_due_date' => 'Valid Until', @@ -803,9 +803,9 @@ return array( 'invoice_charges' => 'Invoice Charges', 'invitation_status' => [ - 'sent' => 'Email Sent', - 'opened' => 'Email Openend', - 'viewed' => 'Invoice Viewed', + 'sent' => 'Email Sent', + 'opened' => 'Email Openend', + 'viewed' => 'Invoice Viewed', ], 'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.', 'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice', @@ -824,5 +824,61 @@ return array( 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'General Settings', 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + + 'enable_with_stripe' => 'Enable | Requires Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Add Tax Rate', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive tax rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Default Tax Rate', + 'tax_rate' => 'Tax Rate', + 'recurring_hour' => 'Recurring Hour', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + + 'payment' => 'Payment', + 'system' => 'System', ); diff --git a/resources/views/clients/edit.blade.php b/resources/views/clients/edit.blade.php index 1891cc6f7..becbfbdd9 100644 --- a/resources/views/clients/edit.blade.php +++ b/resources/views/clients/edit.blade.php @@ -1,4 +1,3 @@ - @extends('header') @@ -7,6 +6,11 @@ @stop @section('content') + +@if ($errors->first('contacts')) +
{{ trans($errors->first('contacts')) }}
+@endif +
{!! Former::open($url) @@ -17,6 +21,7 @@ @if ($client) {!! Former::populate($client) !!} + {!! Former::hidden('public_id') !!} @endif
@@ -75,11 +80,16 @@
- {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('email')->data_bind('value: email, valueUpdate: \'afterkeydown\', attr: {id:\'email\'+$index()}') !!} - {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown'") !!} + {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown', + attr: {name: 'contacts[' + \$index() + '][public_id]'}") !!} + {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown', + attr: {name: 'contacts[' + \$index() + '][first_name]'}") !!} + {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown', + attr: {name: 'contacts[' + \$index() + '][last_name]'}") !!} + {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown', + attr: {name: 'contacts[' + \$index() + '][email]', id:'email'+\$index()}") !!} + {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', + attr: {name: 'contacts[' + \$index() + '][phone]'}") !!}
@@ -141,13 +151,14 @@ self.phone = ko.observable(''); if (data) { - ko.mapping.fromJS(data, {}, this); - } + ko.mapping.fromJS(data, {}, this); + } } - function ContactsModel(data) { + function ClientModel(data) { var self = this; - self.contacts = ko.observableArray(); + + self.contacts = ko.observableArray(); self.mapping = { 'contacts': { @@ -155,10 +166,10 @@ return new ContactModel(options.data); } } - } + } if (data) { - ko.mapping.fromJS(data, self.mapping, this); + ko.mapping.fromJS(data, self.mapping, this); } else { self.contacts.push(new ContactModel()); } @@ -174,7 +185,11 @@ }); } - window.model = new ContactsModel({!! $client !!}); + @if ($data) + window.model = new ClientModel({!! $data !!}); + @else + window.model = new ClientModel({!! $client !!}); + @endif model.showContact = function(elem) { if (elem.nodeType === 1) $(elem).hide().slideDown() } model.hideContact = function(elem) { if (elem.nodeType === 1) $(elem).slideUp(function() { $(elem).remove(); }) } diff --git a/resources/views/clients/show.blade.php b/resources/views/clients/show.blade.php index c04e1f383..bed5942c1 100644 --- a/resources/views/clients/show.blade.php +++ b/resources/views/clients/show.blade.php @@ -25,7 +25,7 @@ {!! Former::open('clients/bulk')->addClass('mainForm') !!}
{!! Former::text('action') !!} - {!! Former::text('id')->value($client->public_id) !!} + {!! Former::text('public_id')->value($client->public_id) !!}
@if ($gatewayLink) diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 247626bf6..bc9c8ff01 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -80,7 +80,7 @@ @foreach ($activities as $activity)
  • {{ Utils::timestampToDateString(strtotime($activity->created_at)) }}: - {!! Utils::decodeActivity($activity->message) !!} + {!! $activity->getMessage() !!}
  • @endforeach diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index b9094cc65..362194c81 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -15,8 +15,7 @@ @stop @section('content') - - @if ($invoice->id) + @if ($invoice->id)
    @@ -406,22 +419,37 @@
    - {!! Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown', attr { placeholder: name.placeholder }")->label('client_name') !!} + {!! Former::hidden('client_public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown', + attr: {name: 'client[public_id]'}") !!} + {!! Former::text('client[name]') + ->data_bind("value: name, valueUpdate: 'afterkeydown', attr { placeholder: name.placeholder }") + ->label('client_name') !!} + - {!! Former::text('id_number')->data_bind("value: id_number, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('vat_number')->data_bind("value: vat_number, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('client[id_number]') + ->label('id_number') + ->data_bind("value: id_number, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('client[vat_number]') + ->label('vat_number') + ->data_bind("value: vat_number, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('website')->data_bind("value: website, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('work_phone')->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('client[website]') + ->label('website') + ->data_bind("value: website, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('client[work_phone]') + ->label('work_phone') + ->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") !!} @if (Auth::user()->isPro()) @if ($account->custom_client_label1) - {!! Former::text('custom_value1')->label($account->custom_client_label1) + {!! Former::text('client[custom_value1]') + ->label($account->custom_client_label1) ->data_bind("value: custom_value1, valueUpdate: 'afterkeydown'") !!} @endif @if ($account->custom_client_label2) - {!! Former::text('custom_value2')->label($account->custom_client_label2) + {!! Former::text('client[custom_value2]') + ->label($account->custom_client_label2) ->data_bind("value: custom_value2, valueUpdate: 'afterkeydown'") !!} @endif @endif @@ -429,13 +457,25 @@   - {!! Former::text('address1')->data_bind("value: address1, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('address2')->data_bind("value: address2, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('city')->data_bind("value: city, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('state')->data_bind("value: state, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('postal_code')->data_bind("value: postal_code, valueUpdate: 'afterkeydown'") !!} - {!! Former::select('country_id')->addOption('','')->addGroupClass('country_select') - ->fromQuery($countries, 'name', 'id')->data_bind("dropdown: country_id") !!} + {!! Former::text('client[address1]') + ->label(trans('texts.address1')) + ->data_bind("value: address1, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('client[address2]') + ->label(trans('texts.address2')) + ->data_bind("value: address2, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('client[city]') + ->label(trans('texts.city')) + ->data_bind("value: city, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('client[state]') + ->label(trans('texts.state')) + ->data_bind("value: state, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('client[postal_code]') + ->label(trans('texts.postal_code')) + ->data_bind("value: postal_code, valueUpdate: 'afterkeydown'") !!} + {!! Former::select('client[country_id]') + ->label(trans('texts.country_id')) + ->addOption('','')->addGroupClass('country_select') + ->fromQuery($countries, 'name', 'id')->data_bind("dropdown: country_id") !!}
    @@ -444,11 +484,18 @@
    - {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('email')->data_bind('value: email, valueUpdate: \'afterkeydown\', attr: {id:\'email\'+$index()}') !!} - {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown'") !!} + + {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown', + attr: {name: 'client[contacts][' + \$index() + '][public_id]'}") !!} + {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown', + attr: {name: 'client[contacts][' + \$index() + '][first_name]'}") !!} + {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown', + attr: {name: 'client[contacts][' + \$index() + '][last_name]'}") !!} + {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown', + attr: {name: 'client[contacts][' + \$index() + '][email]', id:'email'+\$index()}") !!} + {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', + attr: {name: 'client[contacts][' + \$index() + '][phone]'}") !!} +
    @@ -465,24 +512,31 @@   - {!! Former::select('currency_id')->addOption('','') - ->placeholder($account->currency ? $account->currency->name : '') - ->data_bind('value: currency_id') - ->fromQuery($currencies, 'name', 'id') !!} + {!! Former::select('client[currency_id]')->addOption('','') + ->placeholder($account->currency ? $account->currency->name : '') + ->label(trans('texts.currency_id')) + ->data_bind('value: currency_id') + ->fromQuery($currencies, 'name', 'id') !!} - {!! Former::select('language_id')->addOption('','') - ->placeholder($account->language ? $account->language->name : '') - ->data_bind('value: language_id') - ->fromQuery($languages, 'name', 'id') !!} - {!! Former::select('payment_terms')->addOption('','')->data_bind('value: payment_terms') - ->fromQuery($paymentTerms, 'name', 'num_days') - ->help(trans('texts.payment_terms_help')) !!} - {!! Former::select('size_id')->addOption('','')->data_bind('value: size_id') - ->fromQuery($sizes, 'name', 'id') !!} - {!! Former::select('industry_id')->addOption('','')->data_bind('value: industry_id') - ->fromQuery($industries, 'name', 'id') !!} - {!! Former::textarea('private_notes')->data_bind('value: private_notes') !!} + {!! Former::select('client[language_id]')->addOption('','') + ->placeholder($account->language ? $account->language->name : '') + ->label(trans('texts.language_id')) + ->data_bind('value: language_id') + ->fromQuery($languages, 'name', 'id') !!} + {!! Former::select('client[payment_terms]')->addOption('','')->data_bind('value: payment_terms') + ->fromQuery($paymentTerms, 'name', 'num_days') + ->label(trans('texts.payment_terms')) + ->help(trans('texts.payment_terms_help')) !!} + {!! Former::select('client[size_id]')->addOption('','')->data_bind('value: size_id') + ->label(trans('texts.size_id')) + ->fromQuery($sizes, 'name', 'id') !!} + {!! Former::select('client[industry_id]')->addOption('','')->data_bind('value: industry_id') + ->label(trans('texts.industry_id')) + ->fromQuery($industries, 'name', 'id') !!} + {!! Former::textarea('client_private_notes') + ->label(trans('texts.private_notes')) + ->data_bind("value: private_notes, attr:{ name: 'client[private_notes]'}") !!}
    @@ -523,6 +577,14 @@ {!! Former::close() !!} + {!! Former::open("{$entityType}s/bulk")->addClass('bulkForm') !!} + {!! Former::populateField('bulk_public_id', $invoice->public_id) !!} + + {!! Former::text('bulk_public_id') !!} + {!! Former::text('bulk_action') !!} + + {!! Former::close() !!} +
    @include('invoices.knockout') @@ -571,6 +633,7 @@ var invoice = {!! $invoice !!}; ko.mapping.fromJS(invoice, model.invoice().mapping, model.invoice); + model.invoice().is_recurring({{ $invoice->is_recurring ? '1' : '0' }}); @if ($invoice->id) var invitationContactIds = {!! json_encode($invitationContactIds) !!}; @@ -770,7 +833,7 @@ function createInvoiceModel() { var invoice = ko.toJS(window.model).invoice; invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }}; - invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }}; + //invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }}; invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true}); if (invoice.is_recurring) { @@ -909,6 +972,11 @@ $('#submitButton').click(); } + function submitBulkAction(value) { + $('#bulk_action').val(value); + $('.bulkForm').submit(); + } + function isSaveValid() { var isValid = false; for (var i=0; i {!! Former::text('action') !!} {!! Former::text('statusId') !!} - {!! Former::text('id') !!} + {!! Former::text('public_id') !!}
    @if ($entityType == ENTITY_TASK) @@ -66,37 +66,37 @@ } function deleteEntity(id) { - $('#id').val(id); + $('#public_id').val(id); submitForm('delete'); } function archiveEntity(id) { - $('#id').val(id); + $('#public_id').val(id); submitForm('archive'); } function restoreEntity(id) { - $('#id').val(id); + $('#public_id').val(id); submitForm('restore'); } function convertEntity(id) { - $('#id').val(id); + $('#public_id').val(id); submitForm('convert'); } function markEntity(id, statusId) { - $('#id').val(id); + $('#public_id').val(id); $('#statusId').val(statusId); submitForm('mark'); } function stopTask(id) { - $('#id').val(id); + $('#public_id').val(id); submitForm('stop'); } function invoiceTask(id) { - $('#id').val(id); + $('#public_id').val(id); submitForm('invoice'); } diff --git a/resources/views/payments/edit.blade.php b/resources/views/payments/edit.blade.php index 611751ceb..fd6d8e3e0 100644 --- a/resources/views/payments/edit.blade.php +++ b/resources/views/payments/edit.blade.php @@ -12,6 +12,9 @@ {!! Former::populate($payment) !!} @endif + + {!! Former::text('public_id') !!} +
    diff --git a/resources/views/tasks/edit.blade.php b/resources/views/tasks/edit.blade.php index 918baed9f..fde99f631 100644 --- a/resources/views/tasks/edit.blade.php +++ b/resources/views/tasks/edit.blade.php @@ -45,7 +45,7 @@ @if (Auth::user()->account->timezone_id) {{ $timezone }} @else - {!! link_to('/settings/company_details?focus=timezone_id', $timezone, ['target' => '_blank']) !!} + {!! link_to('/settings/localization?focus=timezone_id', $timezone, ['target' => '_blank']) !!} @endif

    @@ -117,6 +117,9 @@ @if ($task && $task->is_running) {!! Button::success(trans('texts.save'))->large()->appendIcon(Icon::create('floppy-disk'))->withAttributes(['id' => 'save-button']) !!} {!! Button::primary(trans('texts.stop'))->large()->appendIcon(Icon::create('stop'))->withAttributes(['id' => 'stop-button']) !!} + @elseif ($task && $task->trashed()) + {!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/tasks'))->appendIcon(Icon::create('remove-circle')) !!} + {!! Button::success(trans('texts.restore'))->large()->withAttributes(['onclick' => 'submitAction("restore")'])->appendIcon(Icon::create('cloud-download')) !!} @else {!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/tasks'))->appendIcon(Icon::create('remove-circle')) !!} @if ($task) diff --git a/tests/acceptance/CheckBalanceCest.php b/tests/acceptance/CheckBalanceCest.php index f96fe85db..eabb30795 100644 --- a/tests/acceptance/CheckBalanceCest.php +++ b/tests/acceptance/CheckBalanceCest.php @@ -25,7 +25,7 @@ class CheckBalanceCest // create client $I->amOnPage('/clients/create'); - $I->fillField(['name' => 'email'], $clientEmail); + $I->fillField(['name' => 'contacts[0][email]'], $clientEmail); $I->click('Save'); $I->wait(1); $I->see($clientEmail); @@ -54,7 +54,7 @@ class CheckBalanceCest // update the invoice $I->amOnPage('/invoices/' . $invoiceId); - $I->fillField(['name' => 'quantity'], 2); + $I->fillField(['name' => 'invoice_items[0][qty]'], 2); $I->click('Save'); $I->wait(1); $I->amOnPage("/clients/{$clientId}"); @@ -69,7 +69,7 @@ class CheckBalanceCest // archive the invoice $I->amOnPage('/invoices/' . $invoiceId); - $I->executeJS('submitAction("archive")'); + $I->executeJS('submitBulkAction("archive")'); $I->wait(1); $I->amOnPage("/clients/{$clientId}"); $I->see('Balance $0.00'); @@ -77,7 +77,7 @@ class CheckBalanceCest // delete the invoice $I->amOnPage('/invoices/' . $invoiceId); - $I->executeJS('submitAction("delete")'); + $I->executeJS('submitBulkAction("delete")'); $I->wait(1); $I->amOnPage("/clients/{$clientId}"); $I->see('Balance $0.00'); diff --git a/tests/acceptance/ClientCest.php b/tests/acceptance/ClientCest.php index 70eb2f8e7..e8b17e8dd 100644 --- a/tests/acceptance/ClientCest.php +++ b/tests/acceptance/ClientCest.php @@ -32,10 +32,10 @@ class ClientCest $I->fillField(['name' => 'work_phone'], $this->faker->phoneNumber); //Contacts - $I->fillField(['name' => 'first_name'], $this->faker->firstName); - $I->fillField(['name' => 'last_name'], $this->faker->lastName); - $I->fillField(['name' => 'email'], $this->faker->companyEmail); - $I->fillField(['name' => 'phone'], $this->faker->phoneNumber); + $I->fillField(['name' => 'contacts[0][first_name]'], $this->faker->firstName); + $I->fillField(['name' => 'contacts[0][last_name]'], $this->faker->lastName); + $I->fillField(['name' => 'contacts[0][email]'], $this->faker->companyEmail); + $I->fillField(['name' => 'contacts[0][phone]'], $this->faker->phoneNumber); //Additional Contact //$I->click('Add contact +'); diff --git a/tests/acceptance/CreditCest.php b/tests/acceptance/CreditCest.php index 6d7f1ac71..3c47d9ede 100644 --- a/tests/acceptance/CreditCest.php +++ b/tests/acceptance/CreditCest.php @@ -24,7 +24,7 @@ class CreditCest $I->wantTo('Create a credit'); $I->amOnPage('/clients/create'); - $I->fillField(['name' => 'email'], $clientEmail); + $I->fillField(['name' => 'contacts[0][email]'], $clientEmail); $I->click('Save'); $I->see($clientEmail); diff --git a/tests/acceptance/InvoiceCest.php b/tests/acceptance/InvoiceCest.php index cd8238b09..5604b5083 100644 --- a/tests/acceptance/InvoiceCest.php +++ b/tests/acceptance/InvoiceCest.php @@ -24,7 +24,7 @@ class InvoiceCest $I->wantTo('create an invoice'); $I->amOnPage('/clients/create'); - $I->fillField(['name' => 'email'], $clientEmail); + $I->fillField(['name' => 'contacts[0][email]'], $clientEmail); $I->click('Save'); $I->see($clientEmail); @@ -52,7 +52,7 @@ class InvoiceCest $I->wantTo('create a recurring invoice'); $I->amOnPage('/clients/create'); - $I->fillField(['name' => 'email'], $clientEmail); + $I->fillField(['name' => 'contacts[0][email]'], $clientEmail); $I->click('Save'); $I->see($clientEmail); diff --git a/tests/acceptance/OnlinePaymentCest.php b/tests/acceptance/OnlinePaymentCest.php index d7526a491..9273bd1d3 100644 --- a/tests/acceptance/OnlinePaymentCest.php +++ b/tests/acceptance/OnlinePaymentCest.php @@ -1,4 +1,4 @@ -amOnPage('/clients/create'); - $I->fillField(['name' => 'email'], $clientEmail); + $I->fillField(['name' => 'contacts[0][email]'], $clientEmail); $I->click('Save'); $I->see($clientEmail); @@ -45,6 +45,7 @@ class OnlinePaymentCest $I->fillField(['name' => 'notes'], $this->faker->text(80)); $I->fillField(['name' => 'cost'], $this->faker->numberBetween(1, 20)); $I->click('Save'); + $I->wait(1); $I->see($productKey); // create invoice diff --git a/tests/acceptance/PaymentCest.php b/tests/acceptance/PaymentCest.php index 275d73909..2808c7c8d 100644 --- a/tests/acceptance/PaymentCest.php +++ b/tests/acceptance/PaymentCest.php @@ -25,7 +25,7 @@ class PaymentCest // create client $I->amOnPage('/clients/create'); - $I->fillField(['name' => 'email'], $clientEmail); + $I->fillField(['name' => 'contacts[0][email]'], $clientEmail); $I->click('Save'); $I->see($clientEmail); @@ -33,7 +33,7 @@ class PaymentCest $I->amOnPage('/products/create'); $I->fillField(['name' => 'product_key'], $productKey); $I->fillField(['name' => 'notes'], $this->faker->text(80)); - $I->fillField(['name' => 'cost'], $this->faker->numberBetween(1, 20)); + $I->fillField(['name' => 'cost'], $this->faker->numberBetween(11, 20)); $I->click('Save'); $I->see($productKey);