@if($item->is_free)
diff --git a/Modules/CircleApps/resources/views/submit.blade.php b/Modules/CircleApps/resources/views/submit.blade.php
index abccb4a..febdd20 100644
--- a/Modules/CircleApps/resources/views/submit.blade.php
+++ b/Modules/CircleApps/resources/views/submit.blade.php
@@ -25,6 +25,12 @@
+
+ @php $apps = \Modules\CircleApps\App\Models\App::where('is_active', true)->get(); @endphp
+ @foreach($apps as $app)
+
+ @endforeach
+
diff --git a/Modules/CircleContacts/App/Providers/CircleContactsServiceProvider.php b/Modules/CircleContacts/App/Providers/CircleContactsServiceProvider.php
index e6414e3..7e96ea6 100644
--- a/Modules/CircleContacts/App/Providers/CircleContactsServiceProvider.php
+++ b/Modules/CircleContacts/App/Providers/CircleContactsServiceProvider.php
@@ -6,6 +6,7 @@
use Illuminate\Support\ServiceProvider;
use Modules\CircleApps\App\Facades\CircleAppsMenu;
use Modules\CircleContacts\App\Console\CircleContactsInstall;
+use Modules\CircleContacts\App\Console\CircleInovicesInstall;
use TomatoPHP\TomatoAdmin\Services\Contracts\Menu;
class CircleContactsServiceProvider extends ServiceProvider
diff --git a/Modules/CircleContacts/resources/views/contacts/index.blade.php b/Modules/CircleContacts/resources/views/contacts/index.blade.php
index 75be1c3..4e88bcc 100644
--- a/Modules/CircleContacts/resources/views/contacts/index.blade.php
+++ b/Modules/CircleContacts/resources/views/contacts/index.blade.php
@@ -21,47 +21,3 @@
@endif
@endsection
-
-{{--
--}}
-{{-- {{ __('CircleXoContact') }}--}}
-
-{{-- --}}
-{{-- {{trans('tomato-admin::global.crud.create-new')}} {{__('CircleXoContact')}}--}}
-{{-- --}}
-
-{{-- --}}
-{{--
--}}
-{{--
--}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{-- --}}
-{{--
--}}
-{{-- --}}
-{{-- --}}
-{{--
--}}
-{{--
--}}
-{{----}}
diff --git a/Modules/CircleInvoices/.github/FUNDING.yml b/Modules/CircleInvoices/.github/FUNDING.yml
new file mode 100644
index 0000000..892ba05
--- /dev/null
+++ b/Modules/CircleInvoices/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: [3x1io]
diff --git a/Modules/CircleInvoices/App/Console/.gitkeep b/Modules/CircleInvoices/App/Console/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/Console/CircleInvoicesInstall.php b/Modules/CircleInvoices/App/Console/CircleInvoicesInstall.php
new file mode 100644
index 0000000..fb55585
--- /dev/null
+++ b/Modules/CircleInvoices/App/Console/CircleInvoicesInstall.php
@@ -0,0 +1,65 @@
+info('Install App');
+ $app = App::where('key', 'circle-invoices')->first();
+ if(!$app){
+ $app = new App();
+ $app->key = 'circle-invoices';
+ $app->name = 'Circle Invoices';
+ $app->description = 'Invoices Generator With Custom Templates, to generate public invoices and your customer can download it';
+ $app->is_active = true;
+ $app->is_free = true;
+ $app->status = "active";
+ $app->homepage = "https://www.github.com/tomatophp/circle-invoices";
+ $app->github = "https://www.github.com/tomatophp/circle-invoices";
+ $app->docs = "https://www.github.com/tomatophp/circle-invoices";
+ $app->privacy = "https://www.github.com/tomatophp/circle-invoices";
+ $app->faq = "https://www.github.com/tomatophp/circle-invoices";
+ $app->email = "info@3x1.io";
+ $getContactsApp = App::where('key', 'circle-contacts')->first();
+ $app->required = [$getContactsApp->id];
+ $app->save();
+ }
+ $this->callSilent('optimize:clear');
+ $this->artisanCommand(["migrate"]);
+ $this->artisanCommand(["optimize:clear"]);
+ $this->info('Circle Contacts App installed successfully.');
+ }
+}
diff --git a/Modules/CircleInvoices/App/Forms/.gitkeep b/Modules/CircleInvoices/App/Forms/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/Http/Controllers/.gitkeep b/Modules/CircleInvoices/App/Http/Controllers/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/Http/Controllers/CircleXoInvoiceController.php b/Modules/CircleInvoices/App/Http/Controllers/CircleXoInvoiceController.php
new file mode 100644
index 0000000..c3c99e9
--- /dev/null
+++ b/Modules/CircleInvoices/App/Http/Controllers/CircleXoInvoiceController.php
@@ -0,0 +1,313 @@
+model = \Modules\CircleInvoices\App\Models\CircleXoInvoice::class;
+ }
+
+ /**
+ * @param Request $request
+ * @return View|JsonResponse
+ */
+ public function index(Request $request): View|JsonResponse
+ {
+ $query = CircleXoInvoice::query();
+ $query->where('account_id', auth('accounts')->user()->id);
+
+ return Tomato::index(
+ request: $request,
+ model: $this->model,
+ view: 'circle-invoices::invoices.index',
+ table: \Modules\CircleInvoices\App\Tables\CircleXoInvoiceTable::class,
+ query: $query
+ );
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ */
+ public function api(Request $request): JsonResponse
+ {
+ $query = CircleXoInvoice::query();
+ $query->where('account_id', auth('accounts')->user()->id);
+ return Tomato::json(
+ request: $request,
+ model: \Modules\CircleInvoices\App\Models\CircleXoInvoice::class,
+ query: $query
+ );
+ }
+
+ /**
+ * @return View
+ */
+ public function create(): View
+ {
+ return Tomato::create(
+ view: 'circle-invoices::invoices.create',
+ );
+ }
+
+ /**
+ * @param \Modules\CircleInvoices\App\Http\Requests\CircleXoInvoice\CircleXoInvoiceStoreRequest $request
+ * @return RedirectResponse|JsonResponse
+ */
+ public function store(\Modules\CircleInvoices\App\Http\Requests\CircleXoInvoice\CircleXoInvoiceStoreRequest $request): RedirectResponse|JsonResponse
+ {
+ $request->merge([
+ "account_id" => auth('accounts')->user()->id,
+ "total" => collect($request->get('items'))->sum('total'),
+ "tax" => collect($request->get('items'))->map(function ($item) {
+ return $item['tax'] * $item['qty'];
+ })->sum(),
+ "discount" => collect($request->get('items'))->map(function ($item) {
+ return $item['discount'] * $item['qty'];
+ })->sum(),
+ ]);
+
+
+ if($request->has('contact_id')){
+ $contact = CircleXoContact::find($request->get('contact_id'));
+ if($contact){
+ $billedTo = $contact->name . "\n" . $contact->address . "\n" . $contact->email . "\n" . $contact->phone . "\n" . $contact->company;
+ }
+
+ $request->merge([
+ "billed_to" => $billedTo ?? null,
+ "shipped_to" => $billedTo ?? null,
+ ]);
+ }
+
+ $response = Tomato::store(
+ request: $request,
+ model: \Modules\CircleInvoices\App\Models\CircleXoInvoice::class,
+ message: __('Invoice saved successfully'),
+ redirect: 'profile.invoices.index',
+ hasMedia: true,
+ collection: [
+ 'logo' => false
+ ]
+ );
+
+ foreach ($request->items as $item) {
+ $response->record->items()->create($item);
+ }
+
+ auth('accounts')->user()->meta('billed_from', $request->billed_from);
+ if($request->hasFile('logo') && auth('accounts')->user()->getMedia('logo')->count() < 1){
+ auth('accounts')->user()->addMedia($request->logo)->toMediaCollection('logo');
+ }
+
+ if($response instanceof JsonResponse){
+ return $response;
+ }
+
+ return redirect()->route('profile.invoices.show', $response->record->id);
+ }
+
+ /**
+ * @param \Modules\CircleInvoices\App\Models\CircleXoInvoice $model
+ * @return View|JsonResponse
+ */
+ public function show(\Modules\CircleInvoices\App\Models\CircleXoInvoice $model): View|JsonResponse
+ {
+ if(!has_app('circle-invoices', $model->account_id)){
+ abort(403);
+ }
+
+ return Tomato::get(
+ model: $model,
+ view: 'circle-invoices::invoices.show',
+ hasMedia: true,
+ collection: [
+ 'logo' => false
+ ]
+ );
+ }
+
+
+ public function print(\Modules\CircleInvoices\App\Models\CircleXoInvoice $model): View|JsonResponse
+ {
+ if(!has_app('circle-invoices', $model->account_id)){
+ abort(403);
+ }
+
+
+ return Tomato::get(
+ model: $model,
+ view: 'circle-invoices::invoices.print',
+ hasMedia: true,
+ collection: [
+ 'logo' => false
+ ]
+ );
+ }
+
+ public function showPublic($invoice): View|JsonResponse
+ {
+ $invoice = CircleXoInvoice::where('uuid', $invoice)->first();
+
+ if($invoice){
+ if($invoice->is_public){
+ return view('circle-invoices::invoices.public', [
+ 'invoice' => $invoice
+ ]);
+ }
+ else {
+ abort(403);
+ }
+ }
+ else {
+ abort(404);
+ }
+
+ }
+
+ public function printPublic($invoice): View|JsonResponse
+ {
+ $invoice = CircleXoInvoice::where('uuid', $invoice)->first();
+
+ if($invoice){
+ if($invoice->is_public){
+ return view('circle-invoices::invoices.print-public', [
+ 'invoice' => $invoice
+ ]);
+ }
+ else {
+ abort(403);
+ }
+ }
+ else {
+ abort(404);
+ }
+ }
+
+ /**
+ * @param \Modules\CircleInvoices\App\Models\CircleXoInvoice $model
+ * @return View
+ */
+ public function edit(\Modules\CircleInvoices\App\Models\CircleXoInvoice $model): View
+ {
+ if(!has_app('circle-invoices', $model->account_id)){
+ abort(403);
+ }
+
+
+ $model->items = $model->items;
+ return Tomato::get(
+ model: $model,
+ view: 'circle-invoices::invoices.edit',
+ hasMedia: true,
+ collection: [
+ 'logo' => false
+ ]
+ );
+ }
+
+ /**
+ * @param \Modules\CircleInvoices\App\Http\Requests\CircleXoInvoice\CircleXoInvoiceUpdateRequest $request
+ * @param \Modules\CircleInvoices\App\Models\CircleXoInvoice $model
+ * @return RedirectResponse|JsonResponse
+ */
+ public function update(\Modules\CircleInvoices\App\Http\Requests\CircleXoInvoice\CircleXoInvoiceUpdateRequest $request, \Modules\CircleInvoices\App\Models\CircleXoInvoice $model): RedirectResponse|JsonResponse
+ {
+ if(!has_app('circle-invoices', $model->account_id)){
+ abort(403);
+ }
+
+
+ $request->merge([
+ "total" => collect($request->get('items'))->sum('total'),
+ "tax" => collect($request->get('items'))->map(function ($item) {
+ return $item['tax'] * $item['qty'];
+ })->sum(),
+ "discount" => collect($request->get('items'))->map(function ($item) {
+ return $item['discount'] * $item['qty'];
+ })->sum(),
+ ]);
+
+ if($request->has('contact_id') && empty($request->billed_to) && empty($request->shipped_to)){
+ $contact = CircleXoContact::find($request->get('contact_id'));
+ if($contact){
+ $billedTo = $contact->name . "\n" . $contact->address . "\n" . $contact->email . "\n" . $contact->phone . "\n" . $contact->company;
+ }
+
+ $request->merge([
+ "billed_to" => $billedTo ?? null,
+ "shipped_to" => $billedTo ?? null,
+ ]);
+ }
+
+
+ $response = Tomato::update(
+ request: $request,
+ model: $model,
+ message: __('Invoice updated successfully'),
+ redirect: 'profile.invoices.index',
+ hasMedia: true,
+ collection: [
+ 'logo' => false
+ ]
+ );
+
+ $response->record->items()->delete();
+ foreach ($request->items as $item) {
+ $response->record->items()->create($item);
+ }
+
+ auth('accounts')->user()->meta('billed_from', $request->billed_from);
+ if($request->hasFile('logo') && $request->file('logo')->getClientOriginalName() !== 'blob'){
+ auth('accounts')->user()->clearMediaCollection('logo');
+ auth('accounts')->user()->addMedia($request->logo)->toMediaCollection('logo');
+ }
+
+ if($response instanceof JsonResponse){
+ return $response;
+ }
+
+ return $response->redirect;
+ }
+
+ /**
+ * @param \Modules\CircleInvoices\App\Models\CircleXoInvoice $model
+ * @return RedirectResponse|JsonResponse
+ */
+ public function destroy(\Modules\CircleInvoices\App\Models\CircleXoInvoice $model): RedirectResponse|JsonResponse
+ {
+ if(!has_app('circle-invoices', $model->account_id)){
+ abort(403);
+ }
+
+ $model->items()->delete();
+ $response = Tomato::destroy(
+ model: $model,
+ message: __('Invoice deleted successfully'),
+ redirect: 'profile.invoices.index',
+ hasMedia: true,
+ collection: [
+ 'logo' => false
+ ]
+ );
+
+ if($response instanceof JsonResponse){
+ return $response;
+ }
+
+ return $response->redirect;
+ }
+}
diff --git a/Modules/CircleInvoices/App/Http/Controllers/CircleXoInvoiceItemController.php b/Modules/CircleInvoices/App/Http/Controllers/CircleXoInvoiceItemController.php
new file mode 100644
index 0000000..7828cfe
--- /dev/null
+++ b/Modules/CircleInvoices/App/Http/Controllers/CircleXoInvoiceItemController.php
@@ -0,0 +1,162 @@
+model = \Modules\CircleInvoices\App\Models\CircleXoInvoiceItem::class;
+ }
+
+ /**
+ * @param Request $request
+ * @return View|JsonResponse
+ */
+ public function index(Request $request): View|JsonResponse
+ {
+ return Tomato::index(
+ request: $request,
+ model: $this->model,
+ view: 'circleinvoices::circle-xo-invoice-items.index',
+ table: \Modules\CircleInvoices\App\Tables\CircleXoInvoiceItemTable::class
+ );
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ */
+ public function api(Request $request): JsonResponse
+ {
+ return Tomato::json(
+ request: $request,
+ model: \Modules\CircleInvoices\App\Models\CircleXoInvoiceItem::class,
+ );
+ }
+
+ /**
+ * @return View
+ */
+ public function create(): View
+ {
+ return Tomato::create(
+ view: 'circleinvoices::circle-xo-invoice-items.create',
+ );
+ }
+
+ /**
+ * @param Request $request
+ * @return RedirectResponse|JsonResponse
+ */
+ public function store(Request $request): RedirectResponse|JsonResponse
+ {
+ $response = Tomato::store(
+ request: $request,
+ model: \Modules\CircleInvoices\App\Models\CircleXoInvoiceItem::class,
+ validation: [
+ 'item' => 'required|max:255|string',
+ 'price' => 'nullable',
+ 'discount' => 'nullable',
+ 'vat' => 'nullable',
+ 'qty' => 'nullable',
+ 'total' => 'nullable',
+ 'is_free' => 'nullable',
+ 'invoice_id' => 'required|exists:circle_xo_invoices,id',
+ 'description' => 'nullable|max:255|string'
+ ],
+ message: __('CircleXoInvoiceItem updated successfully'),
+ redirect: 'admin.circle-xo-invoice-items.index',
+ );
+
+ if($response instanceof JsonResponse){
+ return $response;
+ }
+
+ return $response->redirect;
+ }
+
+ /**
+ * @param \Modules\CircleInvoices\App\Models\CircleXoInvoiceItem $model
+ * @return View|JsonResponse
+ */
+ public function show(\Modules\CircleInvoices\App\Models\CircleXoInvoiceItem $model): View|JsonResponse
+ {
+ return Tomato::get(
+ model: $model,
+ view: 'circleinvoices::circle-xo-invoice-items.show',
+ );
+ }
+
+ /**
+ * @param \Modules\CircleInvoices\App\Models\CircleXoInvoiceItem $model
+ * @return View
+ */
+ public function edit(\Modules\CircleInvoices\App\Models\CircleXoInvoiceItem $model): View
+ {
+ return Tomato::get(
+ model: $model,
+ view: 'circleinvoices::circle-xo-invoice-items.edit',
+ );
+ }
+
+ /**
+ * @param Request $request
+ * @param \Modules\CircleInvoices\App\Models\CircleXoInvoiceItem $model
+ * @return RedirectResponse|JsonResponse
+ */
+ public function update(Request $request, \Modules\CircleInvoices\App\Models\CircleXoInvoiceItem $model): RedirectResponse|JsonResponse
+ {
+ $response = Tomato::update(
+ request: $request,
+ model: $model,
+ validation: [
+ 'item' => 'sometimes|max:255|string',
+ 'price' => 'nullable',
+ 'discount' => 'nullable',
+ 'vat' => 'nullable',
+ 'qty' => 'nullable',
+ 'total' => 'nullable',
+ 'is_free' => 'nullable',
+ 'invoice_id' => 'sometimes|exists:circle_xo_invoices,id',
+ 'description' => 'nullable|max:255|string'
+ ],
+ message: __('CircleXoInvoiceItem updated successfully'),
+ redirect: 'admin.circle-xo-invoice-items.index',
+ );
+
+ if($response instanceof JsonResponse){
+ return $response;
+ }
+
+ return $response->redirect;
+ }
+
+ /**
+ * @param \Modules\CircleInvoices\App\Models\CircleXoInvoiceItem $model
+ * @return RedirectResponse|JsonResponse
+ */
+ public function destroy(\Modules\CircleInvoices\App\Models\CircleXoInvoiceItem $model): RedirectResponse|JsonResponse
+ {
+ $response = Tomato::destroy(
+ model: $model,
+ message: __('CircleXoInvoiceItem deleted successfully'),
+ redirect: 'admin.circle-xo-invoice-items.index',
+ );
+
+ if($response instanceof JsonResponse){
+ return $response;
+ }
+
+ return $response->redirect;
+ }
+}
diff --git a/Modules/CircleInvoices/App/Http/Requests/.gitkeep b/Modules/CircleInvoices/App/Http/Requests/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoice/.gitkeep b/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoice/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoice/CircleXoInvoiceStoreRequest.php b/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoice/CircleXoInvoiceStoreRequest.php
new file mode 100644
index 0000000..1c0e56f
--- /dev/null
+++ b/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoice/CircleXoInvoiceStoreRequest.php
@@ -0,0 +1,51 @@
+
+ */
+ public function rules()
+ {
+ return [
+ 'logo' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
+ 'items' => 'required|array|min:1',
+ 'uuid' => 'required|max:255|string|unique:circle_xo_invoices,uuid',
+ 'billed_from' => 'required|string',
+ 'billed_to' => 'nullable|string',
+ 'shipped_to' => 'nullable|string',
+ 'contact_id' => 'nullable|exists:circle_xo_contacts,id',
+ 'due_date' => 'required|date',
+ 'invoice_date' => 'required|date',
+ 'paid_amount' => 'nullable',
+ 'payment_method' => 'required|max:255|string',
+ 'payment_method_details' => 'nullable',
+ 'total' => 'nullable',
+ 'shipping' => 'nullable',
+ 'discount' => 'nullable',
+ 'tax' => 'nullable',
+ 'type' => 'nullable|max:255|string',
+ 'status' => 'nullable|max:255|string',
+ 'currency' => 'nullable|max:255|string',
+ 'notes' => 'nullable',
+ 'is_public' => 'nullable|boolean',
+ 'template' => 'nullable|max:255|string'
+ ];
+ }
+}
diff --git a/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoice/CircleXoInvoiceUpdateRequest.php b/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoice/CircleXoInvoiceUpdateRequest.php
new file mode 100644
index 0000000..6265dc5
--- /dev/null
+++ b/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoice/CircleXoInvoiceUpdateRequest.php
@@ -0,0 +1,48 @@
+
+ */
+ public function rules()
+ {
+ return [
+ 'billed_from' => 'required|string',
+ 'billed_to' => 'nullable|string',
+ 'shipped_to' => 'nullable|string',
+ 'contact_id' => 'nullable|exists:circle_xo_contacts,id',
+ 'due_date' => 'nullable',
+ 'invoice_date' => 'nullable',
+ 'paid_amount' => 'nullable',
+ 'payment_method' => 'nullable|max:255|string',
+ 'payment_method_details' => 'nullable',
+ 'total' => 'nullable',
+ 'shipping' => 'nullable',
+ 'discount' => 'nullable',
+ 'tax' => 'nullable',
+ 'type' => 'nullable|max:255|string',
+ 'status' => 'nullable|max:255|string',
+ 'currency' => 'nullable|max:255|string',
+ 'notes' => 'nullable',
+ 'is_public' => 'nullable|boolean',
+ 'template' => 'nullable|max:255|string'
+ ];
+ }
+}
diff --git a/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoiceItem/.gitkeep b/Modules/CircleInvoices/App/Http/Requests/CircleXoInvoiceItem/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/Models/.gitkeep b/Modules/CircleInvoices/App/Models/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/Models/CircleXoInvoice.php b/Modules/CircleInvoices/App/Models/CircleXoInvoice.php
new file mode 100644
index 0000000..abb2026
--- /dev/null
+++ b/Modules/CircleInvoices/App/Models/CircleXoInvoice.php
@@ -0,0 +1,88 @@
+ 'json',
+ 'is_public' => 'boolean'
+ ];
+
+
+
+
+ public function account()
+ {
+ return $this->belongsTo(\App\Models\Account::class);
+ }
+
+ public function contact()
+ {
+ return $this->belongsTo(\Modules\CircleContacts\App\Models\CircleXoContact::class);
+ }
+
+ public function items()
+ {
+ return $this->hasMany(\Modules\CircleInvoices\App\Models\CircleXoInvoiceItem::class, 'invoice_id', 'id');
+ }
+}
diff --git a/Modules/CircleInvoices/App/Models/CircleXoInvoiceItem.php b/Modules/CircleInvoices/App/Models/CircleXoInvoiceItem.php
new file mode 100644
index 0000000..f0f44ef
--- /dev/null
+++ b/Modules/CircleInvoices/App/Models/CircleXoInvoiceItem.php
@@ -0,0 +1,47 @@
+ 'boolean'
+ ];
+
+
+
+ public function invoice()
+ {
+ return $this->belongsTo(\Modules\CircleInvoices\App\Models\CircleXoInvoice::class);
+ }
+}
diff --git a/Modules/CircleInvoices/App/Providers/.gitkeep b/Modules/CircleInvoices/App/Providers/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/Providers/CircleInvoicesServiceProvider.php b/Modules/CircleInvoices/App/Providers/CircleInvoicesServiceProvider.php
new file mode 100644
index 0000000..7ad18ec
--- /dev/null
+++ b/Modules/CircleInvoices/App/Providers/CircleInvoicesServiceProvider.php
@@ -0,0 +1,128 @@
+registerCommands();
+ $this->registerCommandSchedules();
+ $this->registerTranslations();
+ $this->registerConfig();
+ $this->registerViews();
+ $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/migrations'));
+
+ CircleAppsMenu::register([
+ \TomatoPHP\TomatoAdmin\Services\Contracts\Menu::make()
+ ->group('circle-invoices')
+ ->label(__('Invoices'))
+ ->icon('bx bxs-receipt')
+ ->route('profile.invoices.index'),
+ ]);
+
+
+ }
+
+ /**
+ * Register the service provider.
+ */
+ public function register(): void
+ {
+ $this->app->register(RouteServiceProvider::class);
+ }
+
+ /**
+ * Register commands in the format of Command::class
+ */
+ protected function registerCommands(): void
+ {
+ $this->commands([
+ CircleInvoicesInstall::class
+ ]);
+ }
+
+ /**
+ * Register command Schedules.
+ */
+ protected function registerCommandSchedules(): void
+ {
+ // $this->app->booted(function () {
+ // $schedule = $this->app->make(Schedule::class);
+ // $schedule->command('inspire')->hourly();
+ // });
+ }
+
+ /**
+ * Register translations.
+ */
+ public function registerTranslations(): void
+ {
+ $langPath = resource_path('lang/modules/'.$this->moduleNameLower);
+
+ if (is_dir($langPath)) {
+ $this->loadTranslationsFrom($langPath, $this->moduleNameLower);
+ $this->loadJsonTranslationsFrom($langPath);
+ } else {
+ $this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
+ $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
+ }
+ }
+
+ /**
+ * Register config.
+ */
+ protected function registerConfig(): void
+ {
+ $this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config');
+ $this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower);
+ }
+
+ /**
+ * Register views.
+ */
+ public function registerViews(): void
+ {
+ $viewPath = resource_path('views/modules/'.$this->moduleNameLower);
+ $sourcePath = module_path($this->moduleName, 'resources/views');
+
+ $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']);
+
+ $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
+
+ $componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.config('modules.paths.generator.component-class.path'));
+ Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
+ }
+
+ /**
+ * Get the services provided by the provider.
+ */
+ public function provides(): array
+ {
+ return [];
+ }
+
+ private function getPublishableViewPaths(): array
+ {
+ $paths = [];
+ foreach (config('view.paths') as $path) {
+ if (is_dir($path.'/modules/'.$this->moduleNameLower)) {
+ $paths[] = $path.'/modules/'.$this->moduleNameLower;
+ }
+ }
+
+ return $paths;
+ }
+}
diff --git a/Modules/CircleInvoices/App/Providers/RouteServiceProvider.php b/Modules/CircleInvoices/App/Providers/RouteServiceProvider.php
new file mode 100644
index 0000000..2154a5d
--- /dev/null
+++ b/Modules/CircleInvoices/App/Providers/RouteServiceProvider.php
@@ -0,0 +1,59 @@
+mapApiRoutes();
+
+ $this->mapWebRoutes();
+ }
+
+ /**
+ * Define the "web" routes for the application.
+ *
+ * These routes all receive session state, CSRF protection, etc.
+ */
+ protected function mapWebRoutes(): void
+ {
+ Route::middleware('web')
+ ->namespace($this->moduleNamespace)
+ ->group(module_path('CircleInvoices', '/routes/web.php'));
+ }
+
+ /**
+ * Define the "api" routes for the application.
+ *
+ * These routes are typically stateless.
+ */
+ protected function mapApiRoutes(): void
+ {
+ Route::prefix('api')
+ ->middleware('api')
+ ->namespace($this->moduleNamespace)
+ ->group(module_path('CircleInvoices', '/routes/api.php'));
+ }
+}
diff --git a/Modules/CircleInvoices/App/Tables/.gitkeep b/Modules/CircleInvoices/App/Tables/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/Tables/CircleXoInvoiceTable.php b/Modules/CircleInvoices/App/Tables/CircleXoInvoiceTable.php
new file mode 100644
index 0000000..7f31806
--- /dev/null
+++ b/Modules/CircleInvoices/App/Tables/CircleXoInvoiceTable.php
@@ -0,0 +1,184 @@
+query = \Modules\CircleInvoices\App\Models\CircleXoInvoice::query();
+ }
+ }
+
+ /**
+ * Determine if the user is authorized to perform bulk actions and exports.
+ *
+ * @return bool
+ */
+ public function authorize(Request $request)
+ {
+ return true;
+ }
+
+ /**
+ * The resource or query builder.
+ *
+ * @return mixed
+ */
+ public function for()
+ {
+ return $this->query;
+ }
+
+ /**
+ * Configure the given SpladeTable.
+ *
+ * @param \ProtoneMedia\Splade\SpladeTable $table
+ * @return void
+ */
+ public function configure(SpladeTable $table)
+ {
+ $table
+ ->withGlobalSearch(
+ label: trans('tomato-admin::global.search'),
+ columns: ['id','uuid','name','email','phone',]
+ )
+ ->bulkAction(
+ label: trans('tomato-admin::global.crud.delete'),
+ each: fn (\Modules\CircleInvoices\App\Models\CircleXoInvoice $model) => $model->delete(),
+ after: fn () => Toast::danger(__('CircleXoInvoice Has Been Deleted'))->autoDismiss(2),
+ confirm: true
+ )
+ ->defaultSort('id', 'desc')
+ ->column(
+ key: 'id',
+ label: __('Id'),
+ sortable: true
+ )
+ ->column(
+ key: 'account_id',
+ label: __('Account id'),
+ sortable: true
+ )
+ ->column(
+ key: 'uuid',
+ label: __('Uuid'),
+ sortable: true
+ )
+ ->column(
+ key: 'name',
+ label: __('Name'),
+ sortable: true
+ )
+ ->column(
+ key: 'email',
+ label: __('Email'),
+ sortable: true
+ )
+ ->column(
+ key: 'phone',
+ label: __('Phone'),
+ sortable: true
+ )
+ ->column(
+ key: 'address',
+ label: __('Address'),
+ sortable: true
+ )
+ ->column(
+ key: 'company',
+ label: __('Company'),
+ sortable: true
+ )
+ ->column(
+ key: 'contact_id',
+ label: __('Contact id'),
+ sortable: true
+ )
+ ->column(
+ key: 'due_date',
+ label: __('Due date'),
+ sortable: true
+ )
+ ->column(
+ key: 'invoice_date',
+ label: __('Invoice date'),
+ sortable: true
+ )
+ ->column(
+ key: 'paid_amount',
+ label: __('Paid amount'),
+ sortable: true
+ )
+ ->column(
+ key: 'payment_method',
+ label: __('Payment method'),
+ sortable: true
+ )
+ ->column(
+ key: 'payment_method_details',
+ label: __('Payment method details'),
+ sortable: true
+ )
+ ->column(
+ key: 'total',
+ label: __('Total'),
+ sortable: true
+ )
+ ->column(
+ key: 'shipping',
+ label: __('Shipping'),
+ sortable: true
+ )
+ ->column(
+ key: 'discount',
+ label: __('Discount'),
+ sortable: true
+ )
+ ->column(
+ key: 'vat',
+ label: __('Vat'),
+ sortable: true
+ )
+ ->column(
+ key: 'type',
+ label: __('Type'),
+ sortable: true
+ )
+ ->column(
+ key: 'status',
+ label: __('Status'),
+ sortable: true
+ )
+ ->column(
+ key: 'currency',
+ label: __('Currency'),
+ sortable: true
+ )
+ ->column(
+ key: 'notes',
+ label: __('Notes'),
+ sortable: true
+ )
+ ->column(
+ key: 'template',
+ label: __('Template'),
+ sortable: true
+ )
+ ->column(key: 'actions',label: trans('tomato-admin::global.crud.actions'))
+ ->export()
+ ->paginate(10);
+ }
+}
diff --git a/Modules/CircleInvoices/App/resources/.gitkeep b/Modules/CircleInvoices/App/resources/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/App/resources/CircleXoInvoicesResource.php b/Modules/CircleInvoices/App/resources/CircleXoInvoicesResource.php
new file mode 100644
index 0000000..d61844d
--- /dev/null
+++ b/Modules/CircleInvoices/App/resources/CircleXoInvoicesResource.php
@@ -0,0 +1,44 @@
+ $this->id,
+ 'uuid' => $this->uuid,
+ 'name' => $this->name,
+ 'email' => $this->email,
+ 'phone' => $this->phone,
+ 'address' => $this->address,
+ 'company' => $this->company,
+ 'contact' => $this->id,
+ 'due_date' => $this->due_date,
+ 'invoice_date' => $this->invoice_date,
+ 'paid_amount' => $this->paid_amount,
+ 'payment_method' => $this->payment_method,
+ 'payment_method_details' => $this->payment_method_details,
+ 'total' => $this->total,
+ 'shipping' => $this->shipping,
+ 'discount' => $this->discount,
+ 'vat' => $this->vat,
+ 'type' => $this->type,
+ 'status' => $this->status,
+ 'currency' => $this->currency,
+ 'notes' => $this->notes,
+ 'template' => $this->template,
+
+ ];
+ }
+}
diff --git a/Modules/CircleInvoices/CHANGELOG.md b/Modules/CircleInvoices/CHANGELOG.md
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/Database/Seeders/.gitkeep b/Modules/CircleInvoices/Database/Seeders/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/Database/Seeders/CircleInvoicesDatabaseSeeder.php b/Modules/CircleInvoices/Database/Seeders/CircleInvoicesDatabaseSeeder.php
new file mode 100644
index 0000000..bd6acfb
--- /dev/null
+++ b/Modules/CircleInvoices/Database/Seeders/CircleInvoicesDatabaseSeeder.php
@@ -0,0 +1,16 @@
+call([]);
+ }
+}
diff --git a/Modules/CircleInvoices/Database/migrations/2024_03_30_02_0303_0505_create_circle_xo_invoices_table.php b/Modules/CircleInvoices/Database/migrations/2024_03_30_02_0303_0505_create_circle_xo_invoices_table.php
new file mode 100644
index 0000000..a16cf04
--- /dev/null
+++ b/Modules/CircleInvoices/Database/migrations/2024_03_30_02_0303_0505_create_circle_xo_invoices_table.php
@@ -0,0 +1,49 @@
+id();
+ $table->foreignId("account_id")->references('id')->on('accounts')->onDelete('cascade');
+ $table->foreignId("contact_id")->nullable()->references('id')->on('circle_xo_contacts')->onDelete('cascade');
+ $table->string("uuid")->unique()->index();
+ $table->text("billed_from")->nullable();
+ $table->text("billed_to")->nullable();
+ $table->text("shipped_to")->nullable();
+ $table->date("due_date")->nullable();
+ $table->date("invoice_date")->nullable();
+ $table->double("paid_amount")->nullable()->unsigned();
+ $table->string("payment_method")->default('cash')->nullable();
+ $table->json("payment_method_details")->nullable();
+ $table->double("total")->nullable()->unsigned();
+ $table->double("shipping")->nullable()->unsigned();
+ $table->double("discount")->nullable()->unsigned();
+ $table->double("tax")->nullable()->unsigned();
+ $table->string("type")->default('invoice')->nullable();
+ $table->string("status")->default('pending')->nullable();
+ $table->string("currency")->default('$')->nullable();
+ $table->text("notes")->nullable();
+ $table->string("template")->default('main')->nullable();
+ $table->boolean('is_public')->default(false)->nullable();
+ $table->timestamps();
+
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('circle_xo_invoices');
+ }
+};
diff --git a/Modules/CircleInvoices/Database/migrations/2024_03_30_02_0303_0808_create_circle_xo_invoice_items_table.php b/Modules/CircleInvoices/Database/migrations/2024_03_30_02_0303_0808_create_circle_xo_invoice_items_table.php
new file mode 100644
index 0000000..6128d55
--- /dev/null
+++ b/Modules/CircleInvoices/Database/migrations/2024_03_30_02_0303_0808_create_circle_xo_invoice_items_table.php
@@ -0,0 +1,37 @@
+id();
+ $table->string("item");
+ $table->double("price")->nullable()->unsigned();
+ $table->double("discount")->nullable()->unsigned();
+ $table->double("tax")->nullable()->unsigned();
+ $table->double("qty")->default(1)->nullable()->unsigned();
+ $table->double("total")->nullable()->unsigned();
+ $table->boolean("is_free")->default(false)->nullable();
+ $table->foreignId("invoice_id")->references('id')->on('circle_xo_invoices')->onDelete('cascade');
+ $table->string("description")->nullable();
+ $table->timestamps();
+
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('circle_xo_invoice_items');
+ }
+};
diff --git a/Modules/CircleInvoices/LICENSE.md b/Modules/CircleInvoices/LICENSE.md
new file mode 100644
index 0000000..a77082f
--- /dev/null
+++ b/Modules/CircleInvoices/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Fady Mondy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Modules/CircleInvoices/README.md b/Modules/CircleInvoices/README.md
new file mode 100644
index 0000000..7c297f8
--- /dev/null
+++ b/Modules/CircleInvoices/README.md
@@ -0,0 +1,29 @@
+# Circle Invoices
+
+Invoices Generator With Custom Templates, to generate public invoices and your customer can download it
+
+## Installation
+
+```bash
+composer require tomatophp/circle-invoices-module
+```
+
+## Support
+
+you can join our discord server to get support [TomatoPHP](https://discord.gg/Xqmt35Uh)
+
+## Docs
+
+you can check docs of this package on [Docs](https://docs.tomatophp.com/plugins/tomato-themes)
+
+## Changelog
+
+Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
+
+## Security
+
+Please see [SECURITY](SECURITY.md) for more information about security.
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
diff --git a/Modules/CircleInvoices/SECURITY.md b/Modules/CircleInvoices/SECURITY.md
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/art/logo.png b/Modules/CircleInvoices/art/logo.png
new file mode 100644
index 0000000..2076c01
Binary files /dev/null and b/Modules/CircleInvoices/art/logo.png differ
diff --git a/Modules/CircleInvoices/composer.json b/Modules/CircleInvoices/composer.json
new file mode 100644
index 0000000..bfb34b0
--- /dev/null
+++ b/Modules/CircleInvoices/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "tomatophp/circle-invoices-module",
+ "type": "laravel-module",
+ "description": "Invoices Generator With Custom Templates, to generate public invoices and your customer can download it",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fady Mondy",
+ "email": "info@3x1.io"
+ }
+ ],
+ "extra": {
+ "laravel": {
+ "providers": [],
+ "aliases": {
+
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Modules\\CircleInvoices\\": "",
+ "Modules\\CircleInvoices\\App\\": "app/",
+ "Modules\\CircleInvoices\\Database\\Factories\\": "database/factories/",
+ "Modules\\CircleInvoices\\Database\\Seeders\\": "database/seeders/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Modules\\CircleInvoices\\Tests\\": "tests/"
+ }
+ }
+}
diff --git a/Modules/CircleInvoices/config/.gitkeep b/Modules/CircleInvoices/config/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/config/config.php b/Modules/CircleInvoices/config/config.php
new file mode 100644
index 0000000..d27252f
--- /dev/null
+++ b/Modules/CircleInvoices/config/config.php
@@ -0,0 +1,5 @@
+ 'CircleInvoices',
+];
diff --git a/Modules/CircleInvoices/module.json b/Modules/CircleInvoices/module.json
new file mode 100644
index 0000000..86c88d3
--- /dev/null
+++ b/Modules/CircleInvoices/module.json
@@ -0,0 +1,29 @@
+{
+ "name": "CircleInvoices",
+ "alias": "circle-invoices",
+ "description": {
+ "ar": "Invoices Generator With Custom Templates, to generate public invoices and your customer can download it",
+ "en": "Invoices Generator With Custom Templates, to generate public invoices and your customer can download it",
+ "gr": "Invoices Generator With Custom Templates, to generate public invoices and your customer can download it",
+ "sp": "Invoices Generator With Custom Templates, to generate public invoices and your customer can download it"
+ },
+ "keywords": [],
+ "priority": 0,
+ "providers": [
+ "Modules\\CircleInvoices\\App\\Providers\\CircleInvoicesServiceProvider"
+ ],
+ "files": [],
+ "title": {
+ "ar": "Circle Invoices",
+ "en": "Circle Invoices",
+ "gr": "Circle Invoices",
+ "sp": "Circle Invoices"
+ },
+ "color": "#2980B9",
+ "icon": "bx bxs-receipt",
+ "placeholder": "placeholder.webp",
+ "type": "plugin",
+ "version": "v1.0",
+ "required": ["CircleContacts"],
+ "apps": ["circle-contacts"]
+}
diff --git a/Modules/CircleInvoices/package.json b/Modules/CircleInvoices/package.json
new file mode 100644
index 0000000..d6fbfc8
--- /dev/null
+++ b/Modules/CircleInvoices/package.json
@@ -0,0 +1,15 @@
+{
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build"
+ },
+ "devDependencies": {
+ "axios": "^1.1.2",
+ "laravel-vite-plugin": "^0.7.5",
+ "sass": "^1.69.5",
+ "postcss": "^8.3.7",
+ "vite": "^4.0.0"
+ }
+}
diff --git a/Modules/CircleInvoices/resources/assets/js/app.js b/Modules/CircleInvoices/resources/assets/js/app.js
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/resources/assets/sass/app.scss b/Modules/CircleInvoices/resources/assets/sass/app.scss
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/resources/views/.gitkeep b/Modules/CircleInvoices/resources/views/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/resources/views/invoices/.gitkeep b/Modules/CircleInvoices/resources/views/invoices/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/resources/views/invoices/create.blade.php b/Modules/CircleInvoices/resources/views/invoices/create.blade.php
new file mode 100644
index 0000000..262b388
--- /dev/null
+++ b/Modules/CircleInvoices/resources/views/invoices/create.blade.php
@@ -0,0 +1,258 @@
+
+@extends('circle-xo::layouts.app')
+
+@section('title', __('New Invoice'))
+
+@section('content')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{__('INVOICE')}}
+
+
+ #
+
+
+
+
+
+
+
{{__('Due Date')}}
+
+
+
+
+
+
{{__('Paid Amount')}}
+
+
+
+
+
+
{{__('Shipping Price')}}
+
+
+
+
+
+
{{__('Payment Method')}}
+
+
+
+
+
+
+
+
+
+
+
{{__('Payment Status')}}
+
+
+
+
+
+
+
+
+
+
{{__('Invoice Type')}}
+
+
+
+
+
+
+
+
+
+
{{__('Currency')}}
+
+ @php $currencies = \Modules\TomatoLocations\App\Models\Currency::all(); @endphp
+
+ @foreach($currencies as $currency)
+
+ @endforeach
+
+
+
+
+
{{__('Invoice Template')}}
+
+
+
+
+
+
+
+
{{__('Invoice Template')}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{__('Item')}}
+
+
+ {{__('Price')}}
+
+
+ {{__('Discount')}}
+
+
+ {{__('Tax')}}
+
+
+ {{__('QTY')}}
+
+
+ {{__('Total')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{__('Tax')}}
+
+
+ @{{ items.tax }}
+
+
+
+
+ {{__('Sub Total')}}
+
+
+ @{{ items.price }}
+
+
+
+
+ {{__('Discount')}}
+
+
+ @{{ items.discount }}
+
+
+
+
+ {{__('Total')}}
+
+
+ @{{ items.total }}
+
+
+
+
+
+
+
+
+
+
+
+
+@endsection
diff --git a/Modules/CircleInvoices/resources/views/invoices/edit.blade.php b/Modules/CircleInvoices/resources/views/invoices/edit.blade.php
new file mode 100644
index 0000000..c28e101
--- /dev/null
+++ b/Modules/CircleInvoices/resources/views/invoices/edit.blade.php
@@ -0,0 +1,246 @@
+@extends('circle-xo::layouts.app')
+
+@section('title', __('Edit Invoice') . ' #' . $model->id)
+
+@section('content')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{__('INVOICE')}}
+
+
+ #
+
+
+
+
+
+
+
{{__('Due Date')}}
+
+
+
+
+
+
{{__('Paid Amount')}}
+
+
+
+
+
+
{{__('Shipping Price')}}
+
+
+
+
+
+
{{__('Payment Method')}}
+
+
+
+
+
+
+
+
+
+
+
{{__('Payment Status')}}
+
+
+
+
+
+
+
+
+
+
{{__('Invoice Type')}}
+
+
+
+
+
+
+
+
+
+
{{__('Currency')}}
+
+ @php $currencies = \Modules\TomatoLocations\App\Models\Currency::all(); @endphp
+
+ @foreach($currencies as $currency)
+
+ @endforeach
+
+
+
+
+
{{__('Invoice Template')}}
+
+
+
+
+
+
+
+
{{__('Invoice Template')}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{__('Item')}}
+
+
+ {{__('Price')}}
+
+
+ {{__('Discount')}}
+
+
+ {{__('Tax')}}
+
+
+ {{__('QTY')}}
+
+
+ {{__('Total')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{__('Tax')}}
+
+
+ @{{ items.tax }}
+
+
+
+
+ {{__('Sub Total')}}
+
+
+ @{{ items.price }}
+
+
+
+
+ {{__('Discount')}}
+
+
+ @{{ items.discount }}
+
+
+
+
+ {{__('Total')}}
+
+
+ @{{ items.total }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+@endsection
diff --git a/Modules/CircleInvoices/resources/views/invoices/index.blade.php b/Modules/CircleInvoices/resources/views/invoices/index.blade.php
new file mode 100644
index 0000000..61d6ba3
--- /dev/null
+++ b/Modules/CircleInvoices/resources/views/invoices/index.blade.php
@@ -0,0 +1,9 @@
+@extends('circle-invoices::layout.app')
+
+@section('content')
+
+
+
+
+
+@endsection
diff --git a/Modules/CircleInvoices/resources/views/invoices/list.blade.php b/Modules/CircleInvoices/resources/views/invoices/list.blade.php
new file mode 100644
index 0000000..d22331b
--- /dev/null
+++ b/Modules/CircleInvoices/resources/views/invoices/list.blade.php
@@ -0,0 +1,103 @@
+@if($table->resource->count() > 0)
+
+ @if(!request()->has('page') || (request()->has('page') && request()->get('page') == 1))
+
+
+
+
+
{{__('Add Invoice')}}
+
+
+
+ @endif
+
+ @foreach($table->resource as $itemKey => $item)
+ @php $itemPrimaryKey = $table->findPrimaryKey($item) @endphp
+
+
+
+
+ {{ $item->uuid }}
+ @if($item->status === 'pending')
+
+ @elseif($item->status === 'active')
+
+ @elseif($item->status === 'paid')
+
+ @endif
+
+
{{($item->total+$item->shipping)}}{{ $item->currency }}
+
+ @if($item->is_public)
+
+
+
+
+
+ @endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{__('Created')}} {{ $item->created_at->diffForHumans() }}
+
+
+
+ @endforeach
+
+@else
+
+
+
+
{{__("You don't have any invoice please add one")}}
+
+
+ {{__('Create Invoice')}}
+
+
+
+
+@endif
diff --git a/Modules/CircleInvoices/resources/views/invoices/print-public.blade.php b/Modules/CircleInvoices/resources/views/invoices/print-public.blade.php
new file mode 100644
index 0000000..38a79e5
--- /dev/null
+++ b/Modules/CircleInvoices/resources/views/invoices/print-public.blade.php
@@ -0,0 +1,174 @@
+
+
+
+
+ @if($invoice->billed_from)
+
+
+ {{__('Bill From')}}:
+
+
+ {{$invoice->billed_from}}
+
+
+ @endif
+ @if($invoice->billed_to)
+
+
+
+ {{__('Bill To')}}:
+
+
+ {{$invoice->billed_to}}
+
+
+
+
+ @endif
+
+
+
+
+
+
{{__('INVOICE')}}
+
+
+ {{__('Invoice')}}# {{$invoice->uuid}}
+
+
+
+
+
+
+
+
{{__('Issue Date')}} :
+
{{$invoice->invoice_date}}
+
+
+
{{__('Due Date')}} :
+
{{$invoice->due_date}}
+
+
+
{{__('Status')}} :
+
{{$invoice->status}}
+
+
+
{{__('Type')}} :
+
{{$invoice->type}}
+
+
+
+
+
+
+
+
+
+
+
+ # |
+ {{__('Item')}} |
+ {{__('Price')}} |
+ {{__('Discount')}} |
+ {{__('Tax')}} |
+ {{__('QTY')}} |
+ {{__('Total')}} |
+
+
+
+ @foreach($invoice->items as $key=>$item)
+
+ {{ $key+1 }} |
+ {{ $item->item }} |
+ {{ number_format($item->price, 2) }}{{ $invoice->currency }} |
+ {{ number_format($item->discount, 2) }}{{ $invoice->currency }} |
+ {{ number_format($item->tax, 2) }}{{ $invoice->currency }} |
+ {{ number_format($item->qty, 2) }} |
+ {{ number_format($item->total, 2) }}{{ $invoice->currency }} |
+
+ @endforeach
+
+
+
+
+
+
+
+
+
+
+ {{__('Sub Total')}}
+
+
+ {{ number_format(($invoice->total + $invoice->discount) - ($invoice->tax + $invoice->shipping), 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Tax')}}
+
+
+ {{ number_format($invoice->tax, 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Discount')}}
+
+
+ {{ number_format($invoice->discount, 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Paid')}}
+
+
+ {{ number_format($invoice->paid_amount, 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Shipping')}}
+
+
+ {{ number_format($invoice->shipping, 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Balance Due')}}
+
+
+ {{ number_format($invoice->total+$invoice->shipping, 2) }}{{ $invoice->currency }}
+
+
+
+
+
+
+ @if($invoice->notes)
+
+
+
+
+ {{__('Notes')}}
+
+
+ {!! $invoice->notes !!}
+
+
+ @endif
+
+
+
+ setTimeout(function(){
+ window.print();
+ }, 1000);
+
+
+
diff --git a/Modules/CircleInvoices/resources/views/invoices/print.blade.php b/Modules/CircleInvoices/resources/views/invoices/print.blade.php
new file mode 100644
index 0000000..e9ab5e7
--- /dev/null
+++ b/Modules/CircleInvoices/resources/views/invoices/print.blade.php
@@ -0,0 +1,174 @@
+
+
+
+
+ @if($model->billed_from)
+
+
+ {{__('Bill From')}}:
+
+
+ {{$model->billed_from}}
+
+
+ @endif
+ @if($model->billed_to)
+
+
+
+ {{__('Bill To')}}:
+
+
+ {{$model->billed_to}}
+
+
+
+
+ @endif
+
+
+
+
+
+
{{__('INVOICE')}}
+
+
+ {{__('Invoice')}}# {{$model->uuid}}
+
+
+
+
+
+
+
+
{{__('Issue Date')}} :
+
{{$model->invoice_date}}
+
+
+
{{__('Due Date')}} :
+
{{$model->due_date}}
+
+
+
{{__('Status')}} :
+
{{$model->status}}
+
+
+
{{__('Type')}} :
+
{{$model->type}}
+
+
+
+
+
+
+
+
+
+
+
+ # |
+ {{__('Item')}} |
+ {{__('Price')}} |
+ {{__('Discount')}} |
+ {{__('Tax')}} |
+ {{__('QTY')}} |
+ {{__('Total')}} |
+
+
+
+ @foreach($model->items as $key=>$item)
+
+ {{ $key+1 }} |
+ {{ $item->item }} |
+ {{ number_format($item->price, 2) }}{{ $model->currency }} |
+ {{ number_format($item->discount, 2) }}{{ $model->currency }} |
+ {{ number_format($item->tax, 2) }}{{ $model->currency }} |
+ {{ number_format($item->qty, 2) }} |
+ {{ number_format($item->total, 2) }}{{ $model->currency }} |
+
+ @endforeach
+
+
+
+
+
+
+
+
+
+
+ {{__('Sub Total')}}
+
+
+ {{ number_format(($model->total + $model->discount) - ($model->tax + $model->shipping), 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Tax')}}
+
+
+ {{ number_format($model->tax, 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Discount')}}
+
+
+ {{ number_format($model->discount, 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Paid')}}
+
+
+ {{ number_format($model->paid_amount, 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Shipping')}}
+
+
+ {{ number_format($model->shipping, 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Balance Due')}}
+
+
+ {{ number_format($model->total+$model->shipping, 2) }}{{ $model->currency }}
+
+
+
+
+
+
+ @if($model->notes)
+
+
+
+
+ {{__('Notes')}}
+
+
+ {!! $model->notes !!}
+
+
+ @endif
+
+
+
+ setTimeout(function(){
+ window.print();
+ }, 1000);
+
+
+
diff --git a/Modules/CircleInvoices/resources/views/invoices/public.blade.php b/Modules/CircleInvoices/resources/views/invoices/public.blade.php
new file mode 100644
index 0000000..e43f1d7
--- /dev/null
+++ b/Modules/CircleInvoices/resources/views/invoices/public.blade.php
@@ -0,0 +1,184 @@
+@seoTitle(__('Invoice') . ' #' . $invoice->uuid . ' ' . __('From') . ' ' . $invoice->account->username . ' | ' . setting('site_name'))
+@seoDescription($invoice->account->bio ?? setting('site_description'))
+@seoKeywords(setting('site_keywords'))
+
+
+
+
+
+
+ {{ __('Invoice') . ' #' . $invoice->uuid . ' ' . __('From') . ' ' . $invoice->account->username }}
+
+
+
+
+
+
+ @if($invoice->billed_from)
+
+
+ {{__('Bill From')}}:
+
+
+ {{$invoice->billed_from}}
+
+
+ @endif
+ @if($invoice->billed_to)
+
+
+
+ {{__('Bill To')}}:
+
+
+ {{$invoice->billed_to}}
+
+
+
+
+ @endif
+
+
+
+
+
+
{{__('INVOICE')}}
+
+
+ {{__('Invoice')}}# {{$invoice->uuid}}
+
+
+
+
+
+
+
+
{{__('Issue Date')}} :
+
{{$invoice->invoice_date}}
+
+
+
{{__('Due Date')}} :
+
{{$invoice->due_date}}
+
+
+
{{__('Status')}} :
+
{{$invoice->status}}
+
+
+
{{__('Type')}} :
+
{{$invoice->type}}
+
+
+
+
+
+
+
+
+
+
+
+ # |
+ {{__('Item')}} |
+ {{__('Price')}} |
+ {{__('Discount')}} |
+ {{__('Tax')}} |
+ {{__('QTY')}} |
+ {{__('Total')}} |
+
+
+
+ @foreach($invoice->items as $key=>$item)
+
+ {{ $key+1 }} |
+ {{ $item->item }} |
+ {{ number_format($item->price, 2) }}{{ $invoice->currency }} |
+ {{ number_format($item->discount, 2) }}{{ $invoice->currency }} |
+ {{ number_format($item->tax, 2) }}{{ $invoice->currency }} |
+ {{ number_format($item->qty, 2) }} |
+ {{ number_format($item->total, 2) }}{{ $invoice->currency }} |
+
+ @endforeach
+
+
+
+
+
+
+
+
+
+ {{__('Sub Total')}}
+
+
+ {{ number_format(($invoice->total + $invoice->discount) - ($invoice->tax + $invoice->shipping), 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Tax')}}
+
+
+ {{ number_format($invoice->tax, 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Discount')}}
+
+
+ {{ number_format($invoice->discount, 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Paid')}}
+
+
+ {{ number_format($invoice->paid_amount, 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Shipping')}}
+
+
+ {{ number_format($invoice->shipping, 2) }}{{ $invoice->currency }}
+
+
+
+
+ {{__('Balance Due')}}
+
+
+ {{ number_format($invoice->total+$invoice->shipping, 2) }}{{ $invoice->currency }}
+
+
+
+
+
+ @if($invoice->notes)
+
+
+
+ {{__('Notes')}}
+
+
+ {!! $invoice->notes !!}
+
+
+ @endif
+
+
+
+
+
+
+
+
+
+
diff --git a/Modules/CircleInvoices/resources/views/invoices/show.blade.php b/Modules/CircleInvoices/resources/views/invoices/show.blade.php
new file mode 100644
index 0000000..a35c0bb
--- /dev/null
+++ b/Modules/CircleInvoices/resources/views/invoices/show.blade.php
@@ -0,0 +1,182 @@
+
+@extends('circle-xo::layouts.app')
+
+@section('title', __('Show Invoice') . ' #' . $model->id)
+
+@section('content')
+
+
+
+
+ @if($model->billed_from)
+
+
+ {{__('Bill From')}}:
+
+
+ {{$model->billed_from}}
+
+
+ @endif
+ @if($model->billed_to)
+
+
+
+ {{__('Bill To')}}:
+
+
+ {{$model->billed_to}}
+
+
+
+
+ @endif
+
+
+
+
+
+
{{__('INVOICE')}}
+
+
+ {{__('Invoice')}}# {{$model->uuid}}
+
+
+
+
+
+
+
+
{{__('Issue Date')}} :
+
{{$model->invoice_date}}
+
+
+
{{__('Due Date')}} :
+
{{$model->due_date}}
+
+
+
{{__('Status')}} :
+
{{$model->status}}
+
+
+
{{__('Type')}} :
+
{{$model->type}}
+
+
+
+
+
+
+
+
+
+
+
+ # |
+ {{__('Item')}} |
+ {{__('Price')}} |
+ {{__('Discount')}} |
+ {{__('Tax')}} |
+ {{__('QTY')}} |
+ {{__('Total')}} |
+
+
+
+ @foreach($model->items as $key=>$item)
+
+ {{ $key+1 }} |
+ {{ $item->item }} |
+ {{ number_format($item->price, 2) }}{{ $model->currency }} |
+ {{ number_format($item->discount, 2) }}{{ $model->currency }} |
+ {{ number_format($item->tax, 2) }}{{ $model->currency }} |
+ {{ number_format($item->qty, 2) }} |
+ {{ number_format($item->total, 2) }}{{ $model->currency }} |
+
+ @endforeach
+
+
+
+
+
+
+
+
+
+ {{__('Sub Total')}}
+
+
+ {{ number_format(($model->total + $model->discount) - ($model->tax + $model->shipping), 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Tax')}}
+
+
+ {{ number_format($model->tax, 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Discount')}}
+
+
+ {{ number_format($model->discount, 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Paid')}}
+
+
+ {{ number_format($model->paid_amount, 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Shipping')}}
+
+
+ {{ number_format($model->shipping, 2) }}{{ $model->currency }}
+
+
+
+
+ {{__('Balance Due')}}
+
+
+ {{ number_format($model->total+$model->shipping, 2) }}{{ $model->currency }}
+
+
+
+
+
+ @if($model->notes)
+
+
+
+ {{__('Notes')}}
+
+
+ {!! $model->notes !!}
+
+
+ @endif
+
+
+
+
+
+
+
+
+@endsection
diff --git a/Modules/CircleInvoices/resources/views/layout/app.blade.php b/Modules/CircleInvoices/resources/views/layout/app.blade.php
new file mode 100644
index 0000000..d78d7de
--- /dev/null
+++ b/Modules/CircleInvoices/resources/views/layout/app.blade.php
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+ {{ __('All Invoices') }}
+
+
+
+
+
+
+
+
+
+ {{ __('Pending') }}
+
+
+
+
+
+
+
+ @yield('content')
+
+
+
diff --git a/Modules/CircleInvoices/resources/views/layout/parts/sidebar.blade.php b/Modules/CircleInvoices/resources/views/layout/parts/sidebar.blade.php
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/routes/.gitkeep b/Modules/CircleInvoices/routes/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/CircleInvoices/routes/api.php b/Modules/CircleInvoices/routes/api.php
new file mode 100644
index 0000000..fbd2221
--- /dev/null
+++ b/Modules/CircleInvoices/routes/api.php
@@ -0,0 +1,15 @@
+name('profile.')->group(function () {
+ Route::get('profile/invoices', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'index'])->name('invoices.index');
+ Route::get('profile/invoices/api', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'api'])->name('invoices.api');
+ Route::get('profile/invoices/create', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'create'])->name('invoices.create');
+ Route::post('profile/invoices', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'store'])->name('invoices.store');
+ Route::get('profile/invoices/{model}', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'show'])->name('invoices.show');
+ Route::get('profile/invoices/{model}/edit', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'edit'])->name('invoices.edit');
+ Route::get('profile/invoices/{model}/print', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'print'])->name('invoices.print');
+ Route::post('profile/invoices/{model}', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'update'])->name('invoices.update');
+ Route::delete('profile/invoices/{model}', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'destroy'])->name('invoices.destroy');
+});
+
+
+Route::middleware(['web', 'splade'])->name('invoices.')->group(function () {
+ Route::get('invoices/{model}/show', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'showPublic'])->name('public.show');
+ Route::get('invoices//{model}/print', [\Modules\CircleInvoices\App\Http\Controllers\CircleXoInvoiceController::class, 'printPublic'])->name('public.print');
+});
+
diff --git a/Modules/CircleInvoices/vite.config.js b/Modules/CircleInvoices/vite.config.js
new file mode 100644
index 0000000..9fd1983
--- /dev/null
+++ b/Modules/CircleInvoices/vite.config.js
@@ -0,0 +1,26 @@
+import { defineConfig } from 'vite';
+import laravel from 'laravel-vite-plugin';
+
+export default defineConfig({
+ build: {
+ outDir: '../../public/build-circleinvoices',
+ emptyOutDir: true,
+ manifest: true,
+ },
+ plugins: [
+ laravel({
+ publicDirectory: '../../public',
+ buildDirectory: 'build-circleinvoices',
+ input: [
+ __dirname + '/resources/assets/sass/app.scss',
+ __dirname + '/resources/assets/js/app.js'
+ ],
+ refresh: true,
+ }),
+ ],
+});
+
+//export const paths = [
+// 'Modules/$STUDLY_NAME$/resources/assets/sass/app.scss',
+// 'Modules/$STUDLY_NAME$/resources/assets/js/app.js',
+//];
\ No newline at end of file
diff --git a/modules_statuses.json b/modules_statuses.json
index 0d296f4..c9a3171 100644
--- a/modules_statuses.json
+++ b/modules_statuses.json
@@ -14,5 +14,6 @@
"TomatoSupport": true,
"TomatoSections": true,
"TomatoMenus": true,
- "CircleContacts": true
-}
+ "CircleContacts": true,
+ "CircleInvoices": true
+}
\ No newline at end of file
diff --git a/resources/views/root.blade.php b/resources/views/root.blade.php
index 1331984..2425108 100644
--- a/resources/views/root.blade.php
+++ b/resources/views/root.blade.php
@@ -11,7 +11,7 @@
}
-
+
@splade