Kimi chinh sua
This commit is contained in:
167
app/Console/Commands/ImportContractsComplex.php
Normal file
167
app/Console/Commands/ImportContractsComplex.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Contract;
|
||||
use App\Models\Product;
|
||||
use App\Models\Customer;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date as ExcelDate;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ImportContractsComplex extends Command
|
||||
{
|
||||
protected $signature = 'import:contracts-complex {hopdong=hopdong.xlsx} {hdkh=Hd_kh.xlsx}';
|
||||
protected $description = 'Import hợp đồng và liên kết khách hàng từ 2 file Excel';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$fileHopDong = $this->argument('hopdong');
|
||||
$fileHdKh = $this->argument('hdkh');
|
||||
|
||||
if (!file_exists($fileHopDong) || !file_exists($fileHdKh)) {
|
||||
$this->error("Không tìm thấy một trong hai file Excel.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// BƯỚC 1: ĐỌC FILE HOPDONG.XLSX ĐỂ LẤY DỮ LIỆU TÀI CHÍNH
|
||||
$this->info("Đang xử lý dữ liệu tài chính từ hopdong.xlsx...");
|
||||
$sheetFinance = IOFactory::load($fileHopDong)->getActiveSheet();
|
||||
$rowsFinance = $sheetFinance->toArray();
|
||||
$financeMap = [];
|
||||
|
||||
foreach ($rowsFinance as $idx => $row) {
|
||||
if ($idx === 0 || empty($row[2])) continue; // Bỏ qua header hoặc Số HĐMB trống
|
||||
|
||||
$contractNumber = trim($row[2]);
|
||||
$financeMap[$contractNumber] = [
|
||||
'signing_date' => $this->parseExcelDate($row[1]),
|
||||
'sale_date' => $this->parseExcelDate($row[3]),
|
||||
'hql_confirmation_date' => $this->parseExcelDate($row[4]),
|
||||
'brokerage_name' => $row[5],
|
||||
'land_value' => $this->parseMoney($row[9]),
|
||||
'foundation_value' => $this->parseMoney($row[10]),
|
||||
'total_value_with_foundation' => $this->parseMoney($row[11]),
|
||||
'stored_contract_count' => (int)$row[23],
|
||||
'filing_note' => $row[26],
|
||||
'discounts' => [
|
||||
'open_sale' => $row[13],
|
||||
'multi_lot' => $row[14],
|
||||
'wholesale' => $row[15],
|
||||
'ctv' => $row[16],
|
||||
'full_payment' => $row[17],
|
||||
'total_percentage' => $row[18],
|
||||
'total_amount' => $this->parseMoney($row[19]),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// BƯỚC 2: ĐỌC FILE HD_KH.XLSX ĐỂ TẠO HỢP ĐỒNG VÀ LIÊN KẾT
|
||||
$this->info("Đang xử lý liên kết khách hàng từ Hd_kh.xlsx...");
|
||||
$sheetLink = IOFactory::load($fileHdKh)->getActiveSheet();
|
||||
$rowsLink = $sheetLink->toArray();
|
||||
|
||||
$count = 0;
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
foreach ($rowsLink as $idx => $row) {
|
||||
if ($idx === 0 || empty($row[2])) continue; // Bỏ qua header hoặc Mã Lô trống
|
||||
|
||||
$plotCode = trim($row[2]);
|
||||
$customerCmnd = trim($row[5]);
|
||||
$transferOrder = (int)$row[3];
|
||||
|
||||
// Tìm sản phẩm
|
||||
$product = Product::where('code', $plotCode)->first();
|
||||
if (!$product) {
|
||||
$this->warn("Bỏ qua: Không tìm thấy Lô {$plotCode} trong database.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Tìm khách hàng
|
||||
$customer = Customer::where('cmnd_cccd', $customerCmnd)->first();
|
||||
if (!$customer) {
|
||||
$this->warn("Bỏ qua: Không tìm thấy Khách hàng CMND {$customerCmnd} ({$row[6]}).");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Logic tìm Hợp đồng tương ứng trong financeMap
|
||||
// Vì hopdong.xlsx không có mã lô, ta sẽ tìm trong financeMap xem Số HĐMB nào có chứa mã lô này
|
||||
$targetContractNumber = null;
|
||||
$financeData = null;
|
||||
foreach ($financeMap as $number => $data) {
|
||||
if (str_contains($number, $plotCode)) {
|
||||
$targetContractNumber = $number;
|
||||
$financeData = $data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$targetContractNumber) {
|
||||
$this->warn("Lô {$plotCode}: Không tìm thấy thông tin tài chính trong hopdong.xlsx. Sẽ dùng mã tạm.");
|
||||
$targetContractNumber = "HD-TEMP-" . $plotCode . "-" . $transferOrder;
|
||||
}
|
||||
|
||||
// Tạo/Cập nhật Hợp đồng
|
||||
$contract = Contract::updateOrCreate(
|
||||
['contract_number' => $targetContractNumber],
|
||||
[
|
||||
'product_id' => $product->id,
|
||||
'signing_date' => $financeData['signing_date'] ?? null,
|
||||
'total_value' => $financeData['total_value_with_foundation'] ?? 0,
|
||||
'land_value' => $financeData['land_value'] ?? 0,
|
||||
'foundation_value' => $financeData['foundation_value'] ?? 0,
|
||||
'total_value_with_foundation' => $financeData['total_value_with_foundation'] ?? 0,
|
||||
'discount_details' => $financeData['discounts'] ?? [],
|
||||
'brokerage_name' => $financeData['brokerage_name'] ?? null,
|
||||
'sale_date' => $financeData['sale_date'] ?? null,
|
||||
'hql_confirmation_date' => $financeData['hql_confirmation_date'] ?? null,
|
||||
'stored_contract_count' => $financeData['stored_contract_count'] ?? 0,
|
||||
'filing_note' => $financeData['filing_note'] ?? null,
|
||||
'transfer_order' => $transferOrder,
|
||||
'contract_type' => 'HĐMB',
|
||||
'status' => 'Đang hiệu lực', // Tạm thời set mặc định
|
||||
]
|
||||
);
|
||||
|
||||
// Liên kết khách hàng (Pivot)
|
||||
$contract->customers()->syncWithoutDetaching([
|
||||
$customer->id => [
|
||||
'role' => $row[7] ?? 'Chủ SH',
|
||||
'transfer_order' => $transferOrder
|
||||
]
|
||||
]);
|
||||
|
||||
$count++;
|
||||
}
|
||||
DB::commit();
|
||||
$this->info("Thành công! Đã tạo và liên kết {$count} bản ghi hợp đồng.");
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
$this->error("Lỗi: " . $e->getMessage());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function parseMoney($value)
|
||||
{
|
||||
if (empty($value)) return 0;
|
||||
return (float) str_replace([',', ' '], '', $value);
|
||||
}
|
||||
|
||||
private function parseExcelDate($value)
|
||||
{
|
||||
if (empty($value)) return null;
|
||||
try {
|
||||
if (is_numeric($value)) {
|
||||
return Carbon::instance(ExcelDate::excelToDateTimeObject($value))->format('Y-m-d');
|
||||
}
|
||||
return Carbon::parse(str_replace('/', '-', $value))->format('Y-m-d');
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
116
app/Console/Commands/ImportCustomersExcel.php
Normal file
116
app/Console/Commands/ImportCustomersExcel.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Customer;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date as ExcelDate;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ImportCustomersExcel extends Command
|
||||
{
|
||||
protected $signature = 'import:customers-excel {file=khachhang.xlsx}';
|
||||
protected $description = 'Import khách hàng từ file Excel và tạo dữ liệu mẫu Công ty';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$filePath = $this->argument('file');
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
$this->error("Không tìm thấy file: {$filePath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("Đang đọc file Excel...");
|
||||
$spreadsheet = IOFactory::load($filePath);
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
$rows = $worksheet->toArray();
|
||||
|
||||
$count = 0;
|
||||
foreach ($rows as $index => $row) {
|
||||
if ($index === 0 || empty($row[1])) continue; // Bỏ qua header hoặc CMND trống
|
||||
|
||||
// 1. Xử lý số điện thoại (Tách nếu có nhiều số)
|
||||
$phoneRaw = $row[5] ?? '';
|
||||
$phones = preg_split('/[,\/ \n]+/', $phoneRaw, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$mainPhone = $phones[0] ?? null;
|
||||
$secondaryPhones = array_slice($phones, 1);
|
||||
|
||||
// 2. Xử lý ngày tháng (Excel thường lưu ngày là số serial)
|
||||
$dob = $this->parseExcelDate($row[4]);
|
||||
$issueDate = $this->parseExcelDate($row[7]);
|
||||
|
||||
Customer::updateOrCreate(
|
||||
['cmnd_cccd' => (string)$row[1]],
|
||||
[
|
||||
'title' => $row[2],
|
||||
'full_name' => $row[3],
|
||||
'dob' => $dob,
|
||||
'phone' => $mainPhone,
|
||||
'secondary_phones' => $secondaryPhones,
|
||||
'email' => $row[6],
|
||||
'id_issue_date' => $issueDate,
|
||||
'id_issue_place' => $row[8],
|
||||
'permanent_address' => $row[9],
|
||||
'contact_address' => $row[10],
|
||||
'type' => 'INDIVIDUAL',
|
||||
]
|
||||
);
|
||||
|
||||
$count++;
|
||||
if ($count % 10 === 0) $this->line("Đã import: {$count} khách hàng...");
|
||||
}
|
||||
|
||||
$this->info("--- TẠO DỮ LIỆU MẪU CÔNG TY ---");
|
||||
$this->createSampleCompany();
|
||||
|
||||
$this->info("Thành công! Đã import {$count} khách hàng và tạo 1 cặp Công ty + Người đại diện mẫu.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function parseExcelDate($value)
|
||||
{
|
||||
if (empty($value)) return null;
|
||||
try {
|
||||
if (is_numeric($value)) {
|
||||
return Carbon::instance(ExcelDate::excelToDateTimeObject($value))->format('Y-m-d');
|
||||
}
|
||||
return Carbon::parse(str_replace('/', '-', $value))->format('Y-m-d');
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function createSampleCompany()
|
||||
{
|
||||
// 1. Tạo người đại diện (Cá nhân)
|
||||
$rep = Customer::updateOrCreate(
|
||||
['cmnd_cccd' => '079083000123'],
|
||||
[
|
||||
'title' => 'Ông',
|
||||
'full_name' => 'NGUYỄN VĂN ĐẠI DIỆN',
|
||||
'phone' => '0909123456',
|
||||
'permanent_address' => '123 Đường ABC, Phường 1, Quận 1, TP.HCM',
|
||||
'contact_address' => '123 Đường ABC, Phường 1, Quận 1, TP.HCM',
|
||||
'type' => 'INDIVIDUAL',
|
||||
]
|
||||
);
|
||||
|
||||
// 2. Tạo công ty liên kết với người đại diện trên
|
||||
Customer::updateOrCreate(
|
||||
['tax_code' => '0102030405'],
|
||||
[
|
||||
'type' => 'COMPANY',
|
||||
'full_name' => 'CÔNG TY TNHH BẤT ĐỘNG SẢN THỊNH VƯỢNG',
|
||||
'cmnd_cccd' => '0102030405', // GPKD
|
||||
'representative_id' => $rep->id,
|
||||
'permanent_address' => '456 Đường XYZ, Phường 2, Quận Tân Bình, TP.HCM', // Trụ sở chính
|
||||
'contact_address' => '456 Đường XYZ, Phường 2, Quận Tân Bình, TP.HCM',
|
||||
'phone' => '02838111222',
|
||||
]
|
||||
);
|
||||
|
||||
$this->info("Đã tạo: Công ty Thịnh Vượng (Đại diện bởi: {$rep->full_name})");
|
||||
}
|
||||
}
|
||||
118
app/Console/Commands/ImportProductsExcel.php
Normal file
118
app/Console/Commands/ImportProductsExcel.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Project;
|
||||
use App\Models\Product;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ImportProductsExcel extends Command
|
||||
{
|
||||
protected $signature = 'import:products-excel {file=sanpham.xlsx}';
|
||||
protected $description = 'Import sản phẩm từ file Excel vào dự án Hà Quang 1';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$filePath = $this->argument('file');
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
$this->error("Không tìm thấy file: {$filePath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("Đang đọc file Excel...");
|
||||
$spreadsheet = IOFactory::load($filePath);
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
$rows = $worksheet->toArray();
|
||||
|
||||
// 1. Đảm bảo có dự án Hà Quang 1
|
||||
$project = Project::firstOrCreate(
|
||||
['name' => 'Hà Quang 1'],
|
||||
['code' => 'HQ1']
|
||||
);
|
||||
|
||||
$this->info("Dự án: {$project->name} (ID: {$project->id})");
|
||||
|
||||
// 2. Duyệt dữ liệu (bỏ qua dòng tiêu đề)
|
||||
$count = 0;
|
||||
foreach ($rows as $index => $row) {
|
||||
if ($index === 0 || empty($row[2])) continue; // Bỏ qua header hoặc dòng trống mã lô
|
||||
|
||||
$code = $row[2];
|
||||
|
||||
// Chuẩn hóa số
|
||||
$area = (float) $row[3];
|
||||
$price_per_unit = $this->parseMoney($row[4]);
|
||||
$total_price = $this->parseMoney($row[5]);
|
||||
$qsdd_value = $this->parseMoney($row[6]);
|
||||
$foundation_temp_value = $this->parseMoney($row[7]);
|
||||
$contract_temp_value = $this->parseMoney($row[8]);
|
||||
|
||||
// Phân tách hạ tầng (JSONB)
|
||||
$infraRaw = $row[14] ?? '';
|
||||
$infraJson = $this->parseInfrastructure($infraRaw);
|
||||
|
||||
// Custom data
|
||||
$customData = [
|
||||
'block' => $row[1],
|
||||
'building_density' => $row[12],
|
||||
'legal_status_raw' => $row[15],
|
||||
'summary_legal' => $row[19],
|
||||
];
|
||||
|
||||
Product::updateOrCreate(
|
||||
['code' => $code, 'project_id' => $project->id],
|
||||
[
|
||||
'product_type' => 'LAND', // Mặc định là đất nền theo file
|
||||
'area' => $area,
|
||||
'price_per_unit' => $price_per_unit,
|
||||
'total_price' => $total_price,
|
||||
'qsdd_value' => $qsdd_value,
|
||||
'foundation_temp_value' => $foundation_temp_value,
|
||||
'contract_temp_value' => $contract_temp_value,
|
||||
'adjacent_road' => $row[9],
|
||||
'frontage_count' => (int) $row[10],
|
||||
'max_floors' => (int) $row[11],
|
||||
'construction_status' => $row[13] ?? 'Chưa xây dựng',
|
||||
'infrastructure_status' => $infraJson,
|
||||
'custom_data' => $customData,
|
||||
'status' => 'Đang mở bán',
|
||||
]
|
||||
);
|
||||
|
||||
$count++;
|
||||
if ($count % 10 === 0) $this->line("Đã import: {$count} sản phẩm...");
|
||||
}
|
||||
|
||||
$this->info("Thành công! Đã import tổng cộng {$count} sản phẩm vào dự án Hà Quang 1.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function parseMoney($value)
|
||||
{
|
||||
if (empty($value)) return 0;
|
||||
// Xóa dấu phẩy và khoảng trắng
|
||||
return (float) str_replace([',', ' '], '', $value);
|
||||
}
|
||||
|
||||
private function parseInfrastructure($raw)
|
||||
{
|
||||
if (empty($raw)) return [];
|
||||
|
||||
$result = [];
|
||||
// Tách theo dấu gạch ngang " - "
|
||||
$parts = explode(' - ', $raw);
|
||||
foreach ($parts as $part) {
|
||||
// Tách theo dấu hai chấm ":"
|
||||
$subParts = explode(':', $part, 2);
|
||||
if (count($subParts) === 2) {
|
||||
$key = trim($subParts[0]);
|
||||
$value = trim($subParts[1]);
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user