feat(portal): lead module, WHMCS sync report/contacts/mapping, service lifecycle email templates
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
3c7710f1de
commit
a443ec3bf6
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Support\CustomerIdResolver;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
|
||||
class ExportMappingCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:export-mapping {--path= : Output .xlsx path (default storage/app/whmcs-mapping-<time>.xlsx)}';
|
||||
|
||||
protected $description = 'Export unresolved WHMCS domains and unusual customer emails (non-gmail / .kgm) to xlsx for manual mapping';
|
||||
|
||||
private const SUSPICIOUS = '/(\.kgm|@(gmail|yahoo|hotmail|outlook)\.com\.[a-z]+)$/i';
|
||||
|
||||
public function handle(CustomerIdResolver $resolver): int
|
||||
{
|
||||
$path = $this->option('path') ?: storage_path('app/whmcs-mapping-' . now()->format('Ymd-His') . '.xlsx');
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$this->sheet($spreadsheet, 0, 'Tên miền chưa có chủ', [
|
||||
'Tên miền', 'WHMCS domain id', 'Trạng thái', 'WHMCS userid (đã xoá)', 'Registrar', 'Hạn (nextduedate)', 'Email liên hệ còn lại', 'Gợi ý email đã sửa', 'Khách portal khớp gợi ý', 'EMAIL KHÁCH CẦN MAP (điền)',
|
||||
], $this->unresolvedDomains($resolver));
|
||||
$this->sheet($spreadsheet, 1, 'Email lạ (.kgm)', [
|
||||
'Nguồn', 'ID', 'Họ tên', 'Công ty', 'Email hiện tại', 'Gợi ý email đã sửa', 'Khách portal khớp gợi ý', 'EMAIL ĐÚNG (điền)',
|
||||
], $this->suspiciousEmails());
|
||||
$this->sheet($spreadsheet, 2, 'Email khác @gmail', [
|
||||
'Nguồn', 'ID', 'Họ tên', 'Công ty', 'Email', 'Tên miền email', 'Có trên portal', 'Có trên WHMCS',
|
||||
], $this->nonGmailEmails());
|
||||
|
||||
(new Xlsx($spreadsheet))->save($path);
|
||||
$this->info("Đã xuất: {$path}");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function unresolvedDomains(CustomerIdResolver $resolver): array
|
||||
{
|
||||
$whmcs = DB::connection('whmcs');
|
||||
$rows = [];
|
||||
$clientIds = $whmcs->table('tblclients')->pluck('id')->flip()->all();
|
||||
|
||||
foreach ($whmcs->table('tbldomains')->orderBy('id')->cursor() as $domain) {
|
||||
if (isset($clientIds[$domain->userid]) || $resolver->resolveDomainOwner((string) $domain->domain, (int) $domain->userid) !== null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$emails = $whmcs->table('tblcontacts')->where('userid', $domain->userid)->pluck('email')->filter()->all();
|
||||
$suggested = array_map(fn ($email) => $this->fixEmail($email), $emails);
|
||||
$rows[] = [
|
||||
trim($domain->domain), $domain->id, $domain->status, $domain->userid, $domain->registrar,
|
||||
$domain->nextduedate, implode('; ', $emails), implode('; ', $suggested), $this->portalMatches($suggested), '',
|
||||
];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
private function suspiciousEmails(): array
|
||||
{
|
||||
$rows = [];
|
||||
foreach ($this->people() as [$source, $id, $name, $company, $email]) {
|
||||
if (! preg_match(self::SUSPICIOUS, $email) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
continue;
|
||||
}
|
||||
$fixed = $this->fixEmail($email);
|
||||
$rows[] = [$source, $id, $name, $company, $email, $fixed, $this->portalMatches([$fixed]), ''];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
private function nonGmailEmails(): array
|
||||
{
|
||||
$portal = DB::table('customers')->pluck('id', DB::raw('LOWER(email)'))->all();
|
||||
$whmcsEmails = DB::connection('whmcs')->table('tblclients')->pluck('id', DB::raw('LOWER(email)'))->all();
|
||||
|
||||
$rows = [];
|
||||
foreach ($this->people() as [$source, $id, $name, $company, $email]) {
|
||||
$domain = strtolower((string) substr(strrchr($email, '@') ?: '', 1));
|
||||
if ($domain === 'gmail.com') {
|
||||
continue;
|
||||
}
|
||||
$key = strtolower($email);
|
||||
$rows[] = [$source, $id, $name, $company, $email, $domain, isset($portal[$key]) ? "#{$portal[$key]}" : 'Không', isset($whmcsEmails[$key]) ? "#{$whmcsEmails[$key]}" : 'Không'];
|
||||
}
|
||||
usort($rows, fn ($a, $b) => [$a[5], $a[4]] <=> [$b[5], $b[4]]);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Generator<int, array{0: string, 1: int, 2: ?string, 3: ?string, 4: string}>
|
||||
*/
|
||||
private function people(): \Generator
|
||||
{
|
||||
foreach (DB::connection('whmcs')->table('tblclients')->orderBy('id')->get(['id', 'firstname', 'lastname', 'companyname', 'email']) as $client) {
|
||||
yield ['WHMCS khách', $client->id, trim("{$client->firstname} {$client->lastname}"), $client->companyname, trim((string) $client->email)];
|
||||
}
|
||||
foreach (DB::table('customers')->orderBy('id')->get(['id', 'fullname', 'company', 'email']) as $customer) {
|
||||
yield ['Portal khách', $customer->id, $customer->fullname, $customer->company, trim((string) $customer->email)];
|
||||
}
|
||||
foreach (DB::connection('whmcs')->table('tblcontacts')->orderBy('id')->get(['id', 'firstname', 'lastname', 'companyname', 'email']) as $contact) {
|
||||
yield ['WHMCS liên hệ', $contact->id, trim("{$contact->firstname} {$contact->lastname}"), $contact->companyname, trim((string) $contact->email)];
|
||||
}
|
||||
}
|
||||
|
||||
private function fixEmail(string $email): string
|
||||
{
|
||||
return preg_replace('/(\.kgm|(?<=@gmail\.com|@yahoo\.com|@hotmail\.com|@outlook\.com)\.[a-z]+)$/i', '', trim($email)) ?? $email;
|
||||
}
|
||||
|
||||
private function portalMatches(array $emails): string
|
||||
{
|
||||
$emails = array_values(array_filter(array_map('strtolower', $emails)));
|
||||
if ($emails === []) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return DB::table('customers')->whereIn(DB::raw('LOWER(email)'), $emails)->get(['id', 'email', 'fullname'])
|
||||
->map(fn ($customer) => "#{$customer->id} {$customer->email} ({$customer->fullname})")->implode('; ');
|
||||
}
|
||||
|
||||
private function sheet(Spreadsheet $spreadsheet, int $index, string $title, array $headers, array $rows): void
|
||||
{
|
||||
$sheet = $index === 0 ? $spreadsheet->getActiveSheet() : $spreadsheet->createSheet($index);
|
||||
$sheet->setTitle($title);
|
||||
|
||||
foreach ($headers as $column => $header) {
|
||||
$sheet->setCellValueExplicit([$column + 1, 1], $header, DataType::TYPE_STRING);
|
||||
}
|
||||
$last = $sheet->getHighestColumn();
|
||||
$sheet->getStyle("A1:{$last}1")->getFont()->setBold(true);
|
||||
$sheet->getStyle("A1:{$last}1")->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB('FED7AA');
|
||||
|
||||
foreach ($rows as $rowIndex => $row) {
|
||||
foreach ($row as $column => $value) {
|
||||
$sheet->setCellValueExplicit([$column + 1, $rowIndex + 2], (string) ($value ?? ''), DataType::TYPE_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (range(1, count($headers)) as $column) {
|
||||
$sheet->getColumnDimensionByColumn($column)->setAutoSize(true);
|
||||
}
|
||||
$sheet->freezePane('A2');
|
||||
$sheet->setAutoFilter("A1:{$last}" . max(1, count($rows) + 1));
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Support\SyncReport;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SyncAllCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-all {--with-domain-sync : Also run domain:sync before whmcs:sync-domains}';
|
||||
protected $signature = 'whmcs:sync-all {--with-domain-sync : Also run domain:sync before whmcs:sync-domains} {--missing-only : Only sync records not yet synced, leaving existing rows untouched}';
|
||||
|
||||
protected $description = 'Run every whmcs:sync-* command in dependency order (clients, products, orders, order-items, invoices, services, domains)';
|
||||
|
||||
@@ -20,8 +21,10 @@ class SyncAllCommand extends Command
|
||||
['command' => 'whmcs:sync-domains', 'forceSync' => false],
|
||||
];
|
||||
|
||||
public function handle(): int
|
||||
public function handle(SyncReport $report): int
|
||||
{
|
||||
$report->beginBatch();
|
||||
|
||||
if ($this->option('with-domain-sync')) {
|
||||
$exitCode = $this->runStep('domain:sync', []);
|
||||
if ($exitCode !== self::SUCCESS) {
|
||||
@@ -33,14 +36,20 @@ class SyncAllCommand extends Command
|
||||
|
||||
foreach (self::STEPS as $step) {
|
||||
$arguments = $step['forceSync'] ? ['--sync' => true] : [];
|
||||
if ($this->option('missing-only') && $step['command'] !== 'whmcs:sync-products') {
|
||||
$arguments['--missing-only'] = true;
|
||||
}
|
||||
$exitCode = $this->runStep($step['command'], $arguments);
|
||||
|
||||
if ($exitCode !== self::SUCCESS) {
|
||||
$report->finishBatch($this);
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('whmcs:sync-all completed successfully.');
|
||||
$report->finishBatch($this);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
@@ -4,24 +4,25 @@ namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\ClientExtractor;
|
||||
use App\Jobs\Whmcs\SyncClientJob;
|
||||
use App\Integrations\Whmcs\Support\SyncReport;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncClientsCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-clients {--sync : Run each record inline instead of queueing it}';
|
||||
protected $signature = 'whmcs:sync-clients {--sync : Run each record inline instead of queueing it} {--missing-only : Only records not yet synced (new, or order without customer)}';
|
||||
|
||||
protected $description = 'Extract clients from the WHMCS database and upsert them into customers';
|
||||
|
||||
public function handle(ClientExtractor $extractor): int
|
||||
{
|
||||
$sync = (bool) $this->option('sync');
|
||||
$total = DB::connection('whmcs')->table('tblclients')->count();
|
||||
$missingOnly = (bool) $this->option('missing-only');
|
||||
$total = $extractor->extract($missingOnly)->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
foreach ($extractor->extract() as $row) {
|
||||
foreach ($extractor->extract($missingOnly) as $row) {
|
||||
$job = new SyncClientJob((array) $row);
|
||||
$sync ? dispatch_sync($job) : dispatch($job);
|
||||
$bar->advance();
|
||||
@@ -31,6 +32,8 @@ class SyncClientsCommand extends Command
|
||||
$this->newLine();
|
||||
$this->info("Dispatched {$total} client(s) for sync.");
|
||||
|
||||
app(SyncReport::class)->finish($this);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Support\CustomerIdResolver;
|
||||
use App\Integrations\Whmcs\Support\SyncReport;
|
||||
use App\Integrations\Whmcs\Writers\LeadWriter;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncContactsCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-contacts';
|
||||
|
||||
protected $description = 'Import WHMCS contacts (tblcontacts) into the Lead module, merging duplicate emails';
|
||||
|
||||
public function handle(CustomerIdResolver $customers, LeadWriter $writer): int
|
||||
{
|
||||
$contacts = DB::connection('whmcs')->table('tblcontacts')->orderBy('id');
|
||||
$bar = $this->output->createProgressBar($contacts->count());
|
||||
|
||||
foreach ($contacts->cursor() as $contact) {
|
||||
$writer->write($contact, $customers->resolve((int) $contact->userid));
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info('Đã nhập liên hệ vào khách tiềm năng (leads).');
|
||||
app(SyncReport::class)->finish($this);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -5,24 +5,25 @@ namespace App\Console\Commands\Whmcs;
|
||||
use App\Integrations\Whmcs\Extractors\CustomerDomainExtractor;
|
||||
use App\Integrations\Whmcs\Transformers\CustomerDomainTransformer;
|
||||
use App\Integrations\Whmcs\Writers\CustomerDomainWriter;
|
||||
use App\Integrations\Whmcs\Support\SyncReport;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncCustomerDomainsCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-domains';
|
||||
protected $signature = 'whmcs:sync-domains {--missing-only : Only records not yet synced (new, or order without customer)}';
|
||||
|
||||
protected $description = 'Extract customer domain registrations (tbldomains) from WHMCS and upsert them into module_domain (run after whmcs:sync-clients, whmcs:sync-orders, domain:sync)';
|
||||
|
||||
public function handle(CustomerDomainExtractor $extractor, CustomerDomainTransformer $transformer, CustomerDomainWriter $writer): int
|
||||
{
|
||||
$total = DB::connection('whmcs')->table('tbldomains')->count();
|
||||
$missingOnly = (bool) $this->option('missing-only');
|
||||
$total = $extractor->extract($missingOnly)->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
$skipped = 0;
|
||||
foreach ($extractor->extract() as $row) {
|
||||
foreach ($extractor->extract($missingOnly) as $row) {
|
||||
if (! $writer->write($transformer->transform($row))) {
|
||||
$skipped++;
|
||||
}
|
||||
@@ -33,6 +34,8 @@ class SyncCustomerDomainsCommand extends Command
|
||||
$this->newLine();
|
||||
$this->info("Processed {$total} domain(s), skipped {$skipped} (unresolved customer).");
|
||||
|
||||
app(SyncReport::class)->finish($this);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,24 +4,25 @@ namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\InvoiceExtractor;
|
||||
use App\Jobs\Whmcs\SyncInvoiceJob;
|
||||
use App\Integrations\Whmcs\Support\SyncReport;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncInvoicesCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-invoices {--sync : Run each record inline instead of queueing it}';
|
||||
protected $signature = 'whmcs:sync-invoices {--sync : Run each record inline instead of queueing it} {--missing-only : Only records not yet synced (new, or order without customer)}';
|
||||
|
||||
protected $description = 'Extract invoices (with their line items) from WHMCS and upsert them into invoices/invoice_items';
|
||||
|
||||
public function handle(InvoiceExtractor $extractor): int
|
||||
{
|
||||
$sync = (bool) $this->option('sync');
|
||||
$total = DB::connection('whmcs')->table('tblinvoices')->count();
|
||||
$missingOnly = (bool) $this->option('missing-only');
|
||||
$total = $extractor->extract($missingOnly)->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
foreach ($extractor->extract() as $row) {
|
||||
foreach ($extractor->extract($missingOnly) as $row) {
|
||||
$job = new SyncInvoiceJob((array) $row);
|
||||
$sync ? dispatch_sync($job) : dispatch($job);
|
||||
$bar->advance();
|
||||
@@ -31,6 +32,8 @@ class SyncInvoicesCommand extends Command
|
||||
$this->newLine();
|
||||
$this->info("Dispatched {$total} invoice(s) for sync.");
|
||||
|
||||
app(SyncReport::class)->finish($this);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,24 +5,25 @@ namespace App\Console\Commands\Whmcs;
|
||||
use App\Integrations\Whmcs\Extractors\OrderItemExtractor;
|
||||
use App\Integrations\Whmcs\Transformers\OrderItemTransformer;
|
||||
use App\Integrations\Whmcs\Writers\OrderItemWriter;
|
||||
use App\Integrations\Whmcs\Support\SyncReport;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncOrderItemsCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-order-items';
|
||||
protected $signature = 'whmcs:sync-order-items {--missing-only : Only records not yet synced (new, or order without customer)}';
|
||||
|
||||
protected $description = 'Extract provisioned services (tblhosting) from WHMCS and upsert them into order_items (run after whmcs:sync-orders)';
|
||||
|
||||
public function handle(OrderItemExtractor $extractor, OrderItemTransformer $transformer, OrderItemWriter $writer): int
|
||||
{
|
||||
$total = DB::connection('whmcs')->table('tblhosting')->count();
|
||||
$missingOnly = (bool) $this->option('missing-only');
|
||||
$total = $extractor->extract($missingOnly)->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
$skipped = 0;
|
||||
foreach ($extractor->extract() as $row) {
|
||||
foreach ($extractor->extract($missingOnly) as $row) {
|
||||
if (! $writer->write($transformer->transform($row))) {
|
||||
$skipped++;
|
||||
}
|
||||
@@ -33,6 +34,8 @@ class SyncOrderItemsCommand extends Command
|
||||
$this->newLine();
|
||||
$this->info("Processed {$total} order item(s), skipped {$skipped} (unresolved order).");
|
||||
|
||||
app(SyncReport::class)->finish($this);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,24 +4,25 @@ namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\OrderExtractor;
|
||||
use App\Jobs\Whmcs\SyncOrderJob;
|
||||
use App\Integrations\Whmcs\Support\SyncReport;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncOrdersCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-orders {--sync : Run each record inline instead of queueing it}';
|
||||
protected $signature = 'whmcs:sync-orders {--sync : Run each record inline instead of queueing it} {--missing-only : Only records not yet synced (new, or order without customer)}';
|
||||
|
||||
protected $description = 'Extract orders from the WHMCS database and upsert them into orders (run before whmcs:sync-invoices)';
|
||||
|
||||
public function handle(OrderExtractor $extractor): int
|
||||
{
|
||||
$sync = (bool) $this->option('sync');
|
||||
$total = DB::connection('whmcs')->table('tblorders')->count();
|
||||
$missingOnly = (bool) $this->option('missing-only');
|
||||
$total = $extractor->extract($missingOnly)->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
foreach ($extractor->extract() as $row) {
|
||||
foreach ($extractor->extract($missingOnly) as $row) {
|
||||
$job = new SyncOrderJob((array) $row);
|
||||
$sync ? dispatch_sync($job) : dispatch($job);
|
||||
$bar->advance();
|
||||
@@ -31,6 +32,8 @@ class SyncOrdersCommand extends Command
|
||||
$this->newLine();
|
||||
$this->info("Dispatched {$total} order(s) for sync.");
|
||||
|
||||
app(SyncReport::class)->finish($this);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,24 +5,25 @@ namespace App\Console\Commands\Whmcs;
|
||||
use App\Integrations\Whmcs\Extractors\ServiceExtractor;
|
||||
use App\Integrations\Whmcs\Transformers\ServiceTransformer;
|
||||
use App\Integrations\Whmcs\Writers\ServiceWriter;
|
||||
use App\Integrations\Whmcs\Support\SyncReport;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncServicesCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-services';
|
||||
protected $signature = 'whmcs:sync-services {--missing-only : Only records not yet synced (new, or order without customer)}';
|
||||
|
||||
protected $description = 'Extract provisioned services (tblhosting) from WHMCS and upsert them into services (run after whmcs:sync-clients, whmcs:sync-products, whmcs:sync-order-items)';
|
||||
|
||||
public function handle(ServiceExtractor $extractor, ServiceTransformer $transformer, ServiceWriter $writer): int
|
||||
{
|
||||
$total = DB::connection('whmcs')->table('tblhosting')->count();
|
||||
$missingOnly = (bool) $this->option('missing-only');
|
||||
$total = $extractor->extract($missingOnly)->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
$skipped = 0;
|
||||
foreach ($extractor->extract() as $row) {
|
||||
foreach ($extractor->extract($missingOnly) as $row) {
|
||||
if (! $writer->write($transformer->transform($row))) {
|
||||
$skipped++;
|
||||
}
|
||||
@@ -33,6 +34,8 @@ class SyncServicesCommand extends Command
|
||||
$this->newLine();
|
||||
$this->info("Processed {$total} service(s), skipped {$skipped} (unresolved customer).");
|
||||
|
||||
app(SyncReport::class)->finish($this);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user