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 018bc2192435..5bc48501167e 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 de4da39cbe71..000000000000 --- 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 fd97865beadc..03b6ce776484 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 000000000000..03ebdc09cd18 --- /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 000000000000..5c2d3700172b --- /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 000000000000..b87063c4979d --- /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 000000000000..385a0472ab4c --- /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 000000000000..7e4790da6885 --- /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 000000000000..2c680905b33a --- /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 000000000000..bc20b312dc5f --- /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 000000000000..e26a5d3ab053 --- /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 000000000000..8d17d961e7ff --- /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 000000000000..1a602c867ea0 --- /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 000000000000..bbf7e23c3353 --- /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 000000000000..7587c071a66e --- /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 cbe08d0528f3..cfd943bcffbf 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 8d9f129e764a..316b1b5c5001 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 000000000000..dc30f6a55869 --- /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 000000000000..35c646612ec9 --- /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 12b5384b35e3..87a0f8f20136 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 4dced73471a0..b8bb693dfc78 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 000000000000..619d33e95890 --- /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 000000000000..e12647c86011 --- /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 000000000000..b4198aab4f45 --- /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 000000000000..954aa83b4cf2 --- /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 000000000000..ce1b87d4a7a8 --- /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 000000000000..3cd84b0e1189 --- /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 000000000000..285a61250c04 --- /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 000000000000..d17ef9c1318c --- /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 000000000000..ce3685d7a212 --- /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 000000000000..19b1ec12d6a5 --- /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 000000000000..0f13a65b437e --- /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 000000000000..f01b9822601f --- /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 d4a358ebcfc9..843107673141 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 39419f227df6..02700e23301d 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 0cc63c7c5a1a..1a2f6c8dc526 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 6b168751cb59..badcdfc35728 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 505400718caf..ad04651acfc9 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 0b72d3669114..a8b31fdb8ab0 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 d3bb9b44bfec..2a9bae80eab8 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 54d74258c771..a8fb60a778d7 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 17d2e548f7cb..d83fcac494c8 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 84a140551851..0ede064b448c 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 fdf9fa36e033..a62b2f2d8594 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 f78a693d66b6..ccf1834a0152 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 0fec382e74df..b357f8d8223a 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 000000000000..2d1716cc1e78 --- /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 000000000000..f2dc44d31aa0 --- /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 000000000000..d0c814687295 --- /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 000000000000..1e18c6b5fea3 --- /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 000000000000..b73e019c4964 --- /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 000000000000..83b192280849 --- /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 000000000000..fe966d47458e --- /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 000000000000..bed71a47f59d --- /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 d072abd00357..000000000000 --- 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 119936e9500d..000000000000 --- 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 47ee62a8585a..000000000000 --- 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 3a49aa9b5af5..000000000000 --- 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 5bc4eab28d97..08961e161752 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 000000000000..35762d8f8678 --- /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 000000000000..aba304457528 --- /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 000000000000..a228dc110508 --- /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 000000000000..7ef7a1116e74 --- /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 000000000000..b52c2fd5f31c --- /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 c768f0bc6fd1..c1637700aacf 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 edb20a7c96e0..fdf41ce54a32 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 000000000000..0ba99f73a284 --- /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 0856a6d431d4..731900e35840 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 9a507bc6bb27..a89e9e9252a0 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 f9a375dc443c..9dd294cacbd8 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 00807b1a6913..821b8b8659c7 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 7a0a8c9535e5..59ea9a60de1e 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 ee382dece1b6..d0a1ae466b91 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 4ccbf9688ab3..fbdd00c46f98 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 ff4f28c51c0f..d6d5e70a184d 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 000000000000..8b06927418d5 --- /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 000000000000..b9e9d0459f61 --- /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 091c66f9c5f6..728b0bccae86 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 700467620294..1ac543c5b281 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 912310c766c6..0e9f9dadf4e3 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 c8f69325c813..67c6d9fe6f1d 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 07e5f03134da..efb642bfeda4 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 739c958686c0..3b7e8007737c 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 000000000000..affb8a235cd6 --- /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 000000000000..fea373d7cb9b --- /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 000000000000..d1e23bfb4f75 --- /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 000000000000..eaa60d999a92 --- /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 b8ad042374ad..e9d99fc7ee9e 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 000000000000..615ea88b49e9 --- /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 71258403c658..85ae4b1df9e1 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 bd0117be2202..4a7945d9a797 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 50bb943c1cf8..1468594b8b47 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 bd46a6106bfd..3027ef372fd1 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 4f350af3fa96..eaff3ef5a356 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 95ef2b5ca6d9..7c59cdc829e3 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 e04f699fce68..8651fb475503 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 1b0e7d822803..50e4f43e288c 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 916f78bc82ac..fece6b644e6e 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 2c20fc364399..9bd47ae4adf0 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 36ad7c3aa8b3..16fd7c7bbb60 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 bacb5e9d3298..2b8b55f98680 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 0bbccacef75d..d524d7f16509 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 ed06fa1c8973..e605fd758f7b 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 1891cc6f7cb0..becbfbdd9572 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 c04e1f383efa..bed5942c14c7 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 247626bf623b..bc9c8ff01a0d 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 b9094cc6511d..362194c81fe9 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 611751ceba21..fd6d8e3e0a54 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 918baed9f214..fde99f6318c4 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 f96fe85dbd35..eabb30795bc8 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 70eb2f8e7d54..e8b17e8dd214 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 6d7f1ac719b5..3c47d9ede362 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 cd8238b097f6..5604b50830d8 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 d7526a491eb4..9273bd1d324f 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 275d73909f53..2808c7c8d33e 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);