diff --git a/add_rows.php b/add_rows.php index 646b45d8..e8c1de87 100755 --- a/add_rows.php +++ b/add_rows.php @@ -52,8 +52,8 @@ return; } -$invoiceId = getPostOrQuery('id', false); -$templateId = getPostOrQuery('from_template', false); +$invoiceId = getPostOrQuery('id'); +$templateId = getPostOrQuery('template_id'); if (!$invoiceId || !$templateId) { echo htmlPageStart(); diff --git a/copy_invoice.php b/copy_invoice.php index fcb4bf3e..447cea67 100755 --- a/copy_invoice.php +++ b/copy_invoice.php @@ -130,46 +130,7 @@ $invoiceData['template_invoice_id'] = $intInvoiceId; } - switch ($invoiceData['interval_type']) { - // Month - case 2: - $invoiceData['next_interval_date'] = date( - 'Ymd', mktime(0, 0, 0, date('m') + 1, date('d'), date('Y')) - ); - break; - // Year - case 3: - $invoiceData['next_interval_date'] = date( - 'Ymd', mktime(0, 0, 0, date('m'), date('d'), date('Y') + 1) - ); - break; - // 2 to 6 months - case 4: - case 5: - case 6: - case 7: - case 8: - $invoiceData['next_interval_date'] = date( - 'Ymd', - mktime( - 0, 0, 0, date('m') + $invoiceData['interval_type'] - 2, - date('d'), date('Y') - ) - ); - break; - // 2 years - case 14: - $invoiceData['next_interval_date'] = date( - 'Ymd', mktime(0, 0, 0, date('m'), date('d'), date('Y') + 2) - ); - break; - // 3 years - case 15: - $invoiceData['next_interval_date'] = date( - 'Ymd', mktime(0, 0, 0, date('m'), date('d'), date('Y') + 3) - ); - break; - } + advanceInvoiceIntervalData($invoiceData); dbQueryCheck('SET AUTOCOMMIT = 0'); dbQueryCheck('BEGIN'); diff --git a/create_invoice_from_template.php b/create_invoice_from_template.php new file mode 100755 index 00000000..c34e1668 --- /dev/null +++ b/create_invoice_from_template.php @@ -0,0 +1,167 @@ + + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://labs.fi/mlinvoice.eng.php + */ +require_once 'htmlfuncs.php'; +require_once 'sqlfuncs.php'; +require_once 'sessionfuncs.php'; + +initDbConnection(); +sesVerifySession(); + +require_once 'translator.php'; +require_once 'datefuncs.php'; +require_once 'miscfuncs.php'; +require_once 'settings.php'; + +if (!sesWriteAccess()) { + echo htmlPageStart(); + ?> + +
+
+ +
+
+ + + + +
+
+ +
+
+ + + + +
+
+ +
+
+ + + 0) { + // Reset interval type of the original invoice + $strQuery = 'UPDATE {prefix}invoice ' . 'SET interval_type = 0 ' . + 'WHERE {prefix}invoice.id = ?'; + dbParamQuery($strQuery, [$intInvoiceId], 'exception'); + } + + $strQuery = 'INSERT INTO {prefix}invoice(' . + implode(', ', array_keys($invoiceData)) . ') ' . 'VALUES (' . + str_repeat('?, ', count($invoiceData) - 1) . '?)'; + + dbParamQuery($strQuery, array_values($invoiceData), 'exception'); + $intNewId = mysqli_insert_id($dblink); + if (!$intNewId) { + die('Could not get ID of the new invoice'); + } + $newRowDate = date('Ymd'); + $strQuery = 'SELECT * ' . 'FROM {prefix}invoice_row ' . + 'WHERE deleted=0 AND invoice_id=?'; + $rows = dbParamQuery($strQuery, [$intInvoiceId], 'exception'); + foreach ($rows as $row) { + if ($boolRefund) { + $row['pcs'] = -$row['pcs']; + if ($row['partial_payment']) { + $row['price'] = -$row['price']; + } + } elseif ($row['reminder_row']) { + continue; + } + unset($row['id']); + $row['invoice_id'] = $intNewId; + + if (getSetting('invoice_update_row_dates_on_copy')) { + $row['row_date'] = $newRowDate; + } + // Update product stock balance + if (!$isOffer && !$isTemplate && $row['product_id'] !== null) { + updateProductStockBalance(null, $row['product_id'], $row['pcs']); + } + $strQuery = 'INSERT INTO {prefix}invoice_row(' . + implode(', ', array_keys($row)) . ') ' . 'VALUES (' . + str_repeat('?, ', count($row) - 1) . '?)'; + dbParamQuery($strQuery, $row, 'exception'); + } + + // Update interval of the template: + $template = getInvoice($templateId); + advanceInvoiceIntervalData($template); + updateInvoice($template); + } catch (Exception $e) { + dbQueryCheck('ROLLBACK'); + dbQueryCheck('SET AUTOCOMMIT = 1'); + die($e->getMessage()); + } + dbQueryCheck('COMMIT'); + dbQueryCheck('SET AUTOCOMMIT = 1'); +} + +header("Location: index.php?func=$strFunc&list=$strList&form=invoice&id=$intNewId"); diff --git a/form.php b/form.php index ff0baaa0..205ce453 100755 --- a/form.php +++ b/form.php @@ -207,7 +207,9 @@ function createForm($strFunc, $strList, $strForm) ) { ?>
  • - + @@ -1120,7 +1122,7 @@ function createFormButtons($form, $formConfig, $new, $top, $deleted)
  • - +
  • diff --git a/lang/en-US.ini b/lang/en-US.ini index 4da7729c..5ac36e81 100644 --- a/lang/en-US.ini +++ b/lang/en-US.ini @@ -283,6 +283,8 @@ RecordsMayBeMissingInUpdate = 'Too many records selected. Cannot update all sele ToggleMenu = 'Show or hide the menu' ServerError = 'Error from server' CannotDeleteCurrentUser = 'Cannot delete currently logged-in user' +RecurringInvoiceDueForProcessing = 'This recurring invoice is due for processing' +RecurringInvoicesDueForProcesing = 'There are recurring invoices due for processing' ; Updater UpdateAvailable = '(update v{version} available)' diff --git a/lang/fi-FI.ini b/lang/fi-FI.ini index 905d40fa..0000998a 100644 --- a/lang/fi-FI.ini +++ b/lang/fi-FI.ini @@ -283,6 +283,8 @@ RecordsMayBeMissingInUpdate = 'Liian monta tietuetta valittu. Kaikkia valittuja ToggleMenu = 'Näytä tai piilota valikko' ServerError = 'Virhe palvelimelta' CannotDeleteCurrentUser = 'Kirjautuneena olevaa käyttäjää ei voida poistaa' +RecurringInvoiceDueForProcessing = 'Tämä toistuva lasku odottaa käsittelyä' +RecurringInvoicesDueForProcesing = 'Käsittelyä odottavia toistuvia laskuja' ; Updater UpdateAvailable = '(päivitys v{version} saatavissa)' diff --git a/lang/sv-FI.ini b/lang/sv-FI.ini index 25656129..5fdb7890 100755 --- a/lang/sv-FI.ini +++ b/lang/sv-FI.ini @@ -285,6 +285,8 @@ RecordsMayBeMissingInUpdate = 'För många utvalda poster. Det går inte att upp ToggleMenu = 'Visa eller dölja menun' ServerError = 'Fel från servern' CannotDeleteCurrentUser = 'Kan inte radera inloggad användare' +RecurringInvoiceDueForProcessing = 'Denna återkommande faktura ska behandlas' +RecurringInvoicesDueForProcesing = 'Det finns återkommande fakturor som ska behandlas' ; Updater UpdateAvailable = '(uppdatering till v{version} tillgänglig)' diff --git a/miscfuncs.php b/miscfuncs.php index 0e49d320..68676c13 100755 --- a/miscfuncs.php +++ b/miscfuncs.php @@ -912,3 +912,47 @@ function getIntervalOptions(): array } return $intervalOptions; } + +function advanceInvoiceIntervalData(array &$invoiceData): void +{ + switch ($invoiceData['interval_type']) { + // Month + case 2: + $invoiceData['next_interval_date'] = date( + 'Ymd', mktime(0, 0, 0, date('m') + 1, date('d'), date('Y')) + ); + break; + // Year + case 3: + $invoiceData['next_interval_date'] = date( + 'Ymd', mktime(0, 0, 0, date('m'), date('d'), date('Y') + 1) + ); + break; + // 2 to 6 months + case 4: + case 5: + case 6: + case 7: + case 8: + $invoiceData['next_interval_date'] = date( + 'Ymd', + mktime( + 0, 0, 0, date('m') + $invoiceData['interval_type'] - 2, + date('d'), date('Y') + ) + ); + break; + // 2 years + case 14: + $invoiceData['next_interval_date'] = date( + 'Ymd', mktime(0, 0, 0, date('m'), date('d'), date('Y') + 2) + ); + break; + // 3 years + case 15: + $invoiceData['next_interval_date'] = date( + 'Ymd', mktime(0, 0, 0, date('m'), date('d'), date('Y') + 3) + ); + break; + } +} diff --git a/search.php b/search.php index 325ba644..1b626734 100755 --- a/search.php +++ b/search.php @@ -4,7 +4,7 @@ * * PHP version 8 * - * Copyright (C) Ere Maijala 2022-2023 + * Copyright (C) Ere Maijala 2022-2024 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, diff --git a/sqlfuncs.php b/sqlfuncs.php index b1e52a84..4ad2250c 100755 --- a/sqlfuncs.php +++ b/sqlfuncs.php @@ -321,6 +321,23 @@ function getInvoiceTemplateId(int $invoiceId): ?int return $rows[0]['template_invoice_id'] ?? null; } +/** + * Get the count of recurring invoice templates in need of processing + * + * @return int + */ +function recurringInvoiceTemplatesNeedProcessing(): int +{ + $rows = dbParamQuery( + 'SELECT count(*) AS cnt FROM {prefix}invoice WHERE state_id IN ' + . '(SELECT id FROM {prefix}invoice_state WHERE invoice_template=1)' + . ' AND next_interval_date <= ?', + [date('Ymd')] + ); + return $rows[0]['cnt']; + +} + /** * Check if an invoice record is open * @@ -556,6 +573,30 @@ function getInvoice($id) return $rows ? $rows[0] : []; } +/** + * Update invoice + * + * @parm array $data Invoice data + * + * @return void + */ +function updateInvoice(array $data): void +{ + $id = $data['id']; + unset($data['id']); + $fields = []; + $params = []; + foreach ($data as $key => $value) { + $fields[] = "$key = ?"; + $params[] = $value; + } + $params[] = $id; + dbParamQuery( + 'UPDATE {prefix}invoice SET ' . implode(', ', $fields) . ' WHERE id=?', + $params + ); +} + /** * Update invoice's print date * diff --git a/start_page.php b/start_page.php index 7a92e567..eeedc855 100755 --- a/start_page.php +++ b/start_page.php @@ -5,7 +5,7 @@ * PHP version 8 * * Copyright (C) Samu Reinikainen 2004-2008 - * Copyright (C) Ere Maijala 2010-2022 + * Copyright (C) Ere Maijala 2010-2024 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -42,6 +42,16 @@ */ function createStartPage() { + // Add notification for recurring invoice templates that need processing: + if (recurringInvoiceTemplatesNeedProcessing()) { + ?> + + getStartPageSearches(); foreach ($searches as $searchId) {