diff --git a/import_expenses.php b/import_expenses.php new file mode 100644 index 0000000..70ade3d --- /dev/null +++ b/import_expenses.php @@ -0,0 +1,189 @@ + [], + 'suppliers' => [], + 'types' => [] + ]; + + $s = $db->query("SELECT id, project_code FROM projects"); + while($r = $s->fetch(PDO::FETCH_ASSOC)) $maps['projects'][strtolower($r['project_code'])] = $r['id']; + + $s = $db->query("SELECT id, name FROM suppliers"); + while($r = $s->fetch(PDO::FETCH_ASSOC)) $maps['suppliers'][strtolower($r['name'])] = $r['id']; + + $s = $db->query("SELECT id, name FROM expense_types"); + while($r = $s->fetch(PDO::FETCH_ASSOC)) $maps['types'][strtolower($r['name'])] = $r['id']; + + return $maps; +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) { + $file = $_FILES['csv_file']; + + if ($file['error'] !== UPLOAD_ERR_OK) { + $error = 'File upload failed.'; + } else { + $handle = fopen($file['tmp_name'], 'r'); + $header = fgetcsv($handle); + + $expected = ['project_code', 'supplier_name', 'expense_type', 'amount', 'date', 'notes']; + + if (!$header || count(array_intersect($expected, $header)) < 3) { + $error = 'Invalid CSV format. Please ensure project_code, supplier_name, and expense_type are present.'; + } else { + $columnMap = array_flip($header); + $maps = getLookupMaps(); + $rowCount = 0; + $importCount = 0; + $skippedCount = 0; + + db()->beginTransaction(); + try { + $stmt = db()->prepare("INSERT INTO expenses (tenant_id, project_id, supplier_id, expense_type_id, amount, allocation_percent, entry_date, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); + + while (($row = fgetcsv($handle)) !== false) { + $rowCount++; + $data = []; + foreach ($expected as $col) { + $data[$col] = isset($columnMap[$col]) && isset($row[$columnMap[$col]]) ? trim($row[$columnMap[$col]]) : ''; + } + + $projId = $maps['projects'][strtolower($data['project_code'])] ?? null; + $suppId = $maps['suppliers'][strtolower($data['supplier_name'])] ?? null; + $typeId = $maps['types'][strtolower($data['expense_type'])] ?? null; + + if (!$projId) { + $results[] = "Row $rowCount: Skipped (Unknown Project Code: " . htmlspecialchars($data['project_code']) . ")"; + $skippedCount++; + continue; + } + if (!$suppId) { + $results[] = "Row $rowCount: Skipped (Unknown Supplier: " . htmlspecialchars($data['supplier_name']) . ")"; + $skippedCount++; + continue; + } + if (!$typeId) { + $results[] = "Row $rowCount: Skipped (Unknown Expense Type: " . htmlspecialchars($data['expense_type']) . ")"; + $skippedCount++; + continue; + } + + $amount = floatval($data['amount']); + $date = empty($data['date']) ? date('Y-m-d') : $data['date']; + + $stmt->execute([ + 1, // tenant_id + $projId, + $suppId, + $typeId, + $amount, + 100.00, // Default allocation_percent + $date, + $data['notes'] + ]); + $importCount++; + } + db()->commit(); + $message = "Import completed. $importCount expenses imported, $skippedCount skipped."; + } catch (Exception $e) { + db()->rollBack(); + $error = 'Database error: ' . $e->getMessage(); + } + } + fclose($handle); + } +} + +include 'includes/header.php'; +?> + +
+
+

Import Expenses

+ + Download Template + +
+ + + + + + + + + +
+
+
+
+
+
+ + +
+ Max file size: 2MB. Only .csv files are allowed. +
+
+ +
+
+
+
+
+
+
+
Import Instructions
+
+
+
    +
  1. Download the CSV template.
  2. +
  3. Ensure project_code, supplier_name, and expense_type match existing records in the system.
  4. +
  5. amount should be a numeric value.
  6. +
  7. date should be in YYYY-MM-DD format (defaults to today if empty).
  8. +
+
+ Rows with unknown projects, suppliers, or types will be skipped. +
+
+
+
+
+ + +
+
+
Import Logs
+
+
+
+ +
+ +
+ +
+
+
+ +
+ + diff --git a/import_clients.php b/import_suppliers.php similarity index 72% rename from import_clients.php rename to import_suppliers.php index 5c851c3..dda6d7a 100644 --- a/import_clients.php +++ b/import_suppliers.php @@ -1,6 +1,6 @@ beginTransaction(); try { - $stmt = db()->prepare("INSERT INTO clients (tenant_id, name, email, phone, address, city, province_state, postal_code, country) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); + $stmt = db()->prepare("INSERT INTO suppliers (tenant_id, name, type, contact_info) VALUES (?, ?, ?, ?)"); while (($row = fgetcsv($handle)) !== false) { $rowCount++; @@ -42,27 +42,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) { $results[] = "Row $rowCount: Skipped (Missing Name)"; continue; } - - // Simple validation: email - if (!empty($data['email']) && !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { - $results[] = "Row $rowCount: Warning (Invalid Email: " . htmlspecialchars($data['email']) . ")"; + + // Default type if invalid + if (!in_array($data['type'], ['supplier', 'contractor'])) { + $data['type'] = 'supplier'; } $stmt->execute([ - 1, // Hardcoded tenant_id for now as per app convention + 1, // Hardcoded tenant_id $data['name'], - $data['email'], - $data['phone'], - $data['address'], - $data['city'], - $data['province_state'], - $data['postal_code'], - $data['country'] + $data['type'], + $data['contact_info'] ]); $importCount++; } db()->commit(); - $message = "Import completed successfully. $importCount clients imported, $skippedCount skipped."; + $message = "Import completed successfully. $importCount suppliers imported, $skippedCount skipped."; } catch (Exception $e) { db()->rollBack(); $error = 'Database error: ' . $e->getMessage(); @@ -77,8 +72,8 @@ include 'includes/header.php';
-

Import Clients

- +

Import Suppliers

+
Download Template
@@ -101,7 +96,7 @@ include 'includes/header.php';
-
+
@@ -124,13 +119,12 @@ include 'includes/header.php';
  1. Download the CSV template using the button above.
  2. -
  3. Fill in your client data, ensuring the name field is not empty.
  4. -
  5. Save the file as a CSV (Comma Separated Values).
  6. -
  7. Upload the file using the form on the left.
  8. -
  9. Review any warnings or errors that appear after processing.
  10. +
  11. Fill in your supplier data. The name field is required.
  12. +
  13. The type field should be either 'supplier' or 'contractor'.
  14. +
  15. Save the file as a CSV and upload it.
- Existing clients with the same name will be added as new entries. + Existing suppliers with the same name will be added as new entries.
diff --git a/includes/header.php b/includes/header.php index 8fb1562..d3d4a35 100644 --- a/includes/header.php +++ b/includes/header.php @@ -67,14 +67,15 @@ $currentPage = basename($_SERVER['PHP_SELF']); diff --git a/samples/clients_template.csv b/samples/clients_template.csv deleted file mode 100644 index 90a61ef..0000000 --- a/samples/clients_template.csv +++ /dev/null @@ -1,3 +0,0 @@ -name,email,phone,address,city,province_state,postal_code,country -Acme Corp,contact@acme.com,555-0123,123 Industrial Way,Tech City,ON,M5V 2N2,Canada -Global Tech,info@globaltech.io,555-9876,456 Innovation Blvd,Futureville,QC,H3B 1A1,Canada diff --git a/samples/expenses_template.csv b/samples/expenses_template.csv new file mode 100644 index 0000000..5091c43 --- /dev/null +++ b/samples/expenses_template.csv @@ -0,0 +1,4 @@ +project_code,supplier_name,expense_type,amount,date,notes +P001,ACME Corp,Materials,1250.00,2026-02-10,Lumber for site A +P002,Build-It Co,Subcontractors,5000.00,2026-02-12,Foundation work +P001,Global Telecom,Overhead,150.00,2026-02-15,Monthly internet diff --git a/samples/suppliers_template.csv b/samples/suppliers_template.csv new file mode 100644 index 0000000..34f55b2 --- /dev/null +++ b/samples/suppliers_template.csv @@ -0,0 +1,4 @@ +name,type,contact_info +ACME Corp,supplier,admin@acme.com +Build-It Co,contractor,contact@buildit.com +Global Telecom,supplier,info@globaltelecom.com diff --git a/settings.php b/settings.php index a2a7f3e..0e3018b 100644 --- a/settings.php +++ b/settings.php @@ -240,8 +240,12 @@ include __DIR__ . '/includes/header.php';

Import legacy data or bulk records from other systems using CSV templates.