diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index 58400ba..03d4566 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -2,9 +2,9 @@ ## Milestone v0.1: XXXV Konferencja Registration Update -Status: Complete +Status: In progress -Progress: 2 of 2 phases complete (100%) +Progress: 2 of 3 phases complete (67%) ### Phase 1: Registration Form Update Status: Complete @@ -23,3 +23,11 @@ Goal: Move new and related registration phrases into the editable dictionary so Planned: - `02-01`: Complete - seeded dictionary phrases and updated registration templates to use translations. + +### Phase 3: Registration Form Settings +Status: Applying + +Goal: Add an administrator "Ustawienia formularza" page for editable registration participation days and all prices used by the public form, mail/summary output, and server-side price calculation. + +Planned: +- `03-01`: Applied, awaiting unify - created settings helper, admin page, deployment seed, and connected public form pricing/day options to editable settings. diff --git a/.paul/STATE.md b/.paul/STATE.md index 9e80c3e..fd8fa2c 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -1,29 +1,29 @@ ## Current Position Milestone: v0.1 XXXV Konferencja Registration Update -Phase: 2 of 2 (Registration Dictionary Phrases) - Complete -Plan: 02-01 complete -Status: UNIFY complete, loop closed -Last activity: 2026-04-24 21:25:19 +02:00 - Created `.paul/phases/02-registration-dictionary-phrases/02-01-SUMMARY.md` +Phase: 3 of 3 (Registration Form Settings) - Planning +Plan: 03-01 applied, awaiting unify +Status: APPLY complete, ready for UNIFY +Last activity: 2026-04-24 23:09:13 +02:00 - Added in-plan KSeF helper note under NIP field Progress: -- Milestone: [##########] 100% -- Phase 2: [##########] 100% +- Milestone: [#######---] 67% +- Phase 3: [----------] 0% ## Loop Position Current loop state: ```text PLAN --> APPLY --> UNIFY - * * * [Loop complete - milestone complete] + * * o [Apply complete, awaiting unify] ``` ## Session Continuity -Last session: 2026-04-24 21:25:19 +02:00 -Stopped at: Plan 02-01 unified -Next action: Apply dictionary seed on target server, then deploy/test as needed -Resume file: `.paul/phases/02-registration-dictionary-phrases/02-01-SUMMARY.md` +Last session: 2026-04-24 23:09:13 +02:00 +Stopped at: Plan 03-01 applied +Next action: Run $paul-unify `.paul/phases/03-registration-form-settings/03-01-PLAN.md` +Resume file: `.paul/phases/03-registration-form-settings/03-01-APPLY.md` ## Accumulated Context @@ -32,9 +32,11 @@ Resume file: `.paul/phases/02-registration-dictionary-phrases/02-01-SUMMARY.md` |----------|-------|--------| | 2026-04-24: Use stable `registration_*` dictionary keys for registration-specific phrases | Phase 2 | Admin can edit visible registration wording without template changes. | | 2026-04-24: Preserve form values, persistence, pricing logic, and payment status values during dictionary refactor | Phase 2 | Registration behavior remains aligned with Phase 1. | +| 2026-04-24: Plan form settings around `wp_setup` for new scalar/list settings and `mf_parameters` for existing price rows | Phase 3 | Avoids schema changes while exposing current calculation values in one admin screen. | ### Blockers/Concerns - Production must run `_rejestracja/sql/apply-2026-04-24-registration-dictionary-phrases.php?run=20260424` or apply the SQL file before dictionary entries are visible/editable. +- Phase 3 will require applying `_rejestracja/sql/apply-2026-04-24-registration-form-settings.php?run=20260424` or the matching SQL seed after implementation. ### Deferred Issues - None. diff --git a/.paul/phases/03-registration-form-settings/03-01-APPLY.md b/.paul/phases/03-registration-form-settings/03-01-APPLY.md new file mode 100644 index 0000000..3ecc670 --- /dev/null +++ b/.paul/phases/03-registration-form-settings/03-01-APPLY.md @@ -0,0 +1,51 @@ +# APPLY Results: 03-01 + +Date: 2026-04-24 23:04:32 +02:00 + +## Completed Tasks + +### Task 1: Add Shared Registration Settings Helper +Status: pass + +Notes: +- Added `_rejestracja/core/class/RegistrationFormSettings.class.php` to centralize participation day lists, VAT multiplier, one-day prices, and existing `mf_parameters` price rows. +- Added `_rejestracja/sql/2026-04-24-registration-form-settings.sql` with idempotent `wp_setup` inserts. +- Added `_rejestracja/sql/apply-2026-04-24-registration-form-settings.php` as a guarded browser/CLI seed runner. +- New list/scalar settings use `wp_setup`; existing calculator prices remain in `mf_parameters`. + +### Task 2: Build Admin Form Settings Page +Status: pass + +Notes: +- Added `_rejestracja/Admin/controller/FormSettingsController.php`. +- Added `_rejestracja/Admin/template/partial/FormSettings/Index.tpl`. +- Added the `Ustawienia formularza` admin menu link to Dictionary, Setup, and the new controller. +- The admin form can edit participation days, one-day prices, VAT multiplier, and relevant `mf_parameters` price rows. + +### Task 3: Use Settings in Public Form and Submit Pricing +Status: pass + +Notes: +- Updated `_rejestracja/controller/IndexController.php` to load registration settings, pass them to Smarty/JavaScript, calculate VAT from the configured multiplier, and save one-day participant prices from settings. +- Updated `_rejestracja/template/partial/Index/Index.tpl` to render configured `participation_days` lists and use configured one-day prices in `calculatePrice`. +- Added a small KSeF ID helper note under the public NIP field as an in-plan follow-up. +- Preserved public field names and stored values for existing registration flow compatibility. + +## Verification + +- `php -l _rejestracja/core/class/RegistrationFormSettings.class.php`: pass +- `php -l _rejestracja/Admin/controller/FormSettingsController.php`: pass +- `php -l _rejestracja/controller/IndexController.php`: pass +- `php -l _rejestracja/sql/apply-2026-04-24-registration-form-settings.php`: pass +- Hardcoded day/one-day price scan: pass; matches remain only in helper defaults and deployment seed files. +- Admin menu scan: pass; Dictionary, Setup, and FormSettings include `Ustawienia formularza`. +- NIP field helper note: pass; rendered under the existing NIP input without changing form field names. + +## Deviations + +- No schema changes were introduced; the plan's no-schema approach was followed. +- Unrelated working tree entries were present and left untouched: `_rejestracja/Static/image/Admin/Thumbs.db` and `.vscode/ftp-kr.sync.cache.json`. + +## Next Step + +Run `$paul-unify .paul/phases/03-registration-form-settings/03-01-PLAN.md` to reconcile the plan and close the loop. diff --git a/.paul/phases/03-registration-form-settings/03-01-PLAN.md b/.paul/phases/03-registration-form-settings/03-01-PLAN.md new file mode 100644 index 0000000..60bd90a --- /dev/null +++ b/.paul/phases/03-registration-form-settings/03-01-PLAN.md @@ -0,0 +1,179 @@ +--- +phase: 03-registration-form-settings +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - _rejestracja/core/class/RegistrationFormSettings.class.php + - _rejestracja/Admin/controller/FormSettingsController.php + - _rejestracja/Admin/template/partial/FormSettings/Index.tpl + - _rejestracja/Admin/controller/DictionaryController.php + - _rejestracja/Admin/controller/SetupController.php + - _rejestracja/controller/IndexController.php + - _rejestracja/template/partial/Index/Index.tpl + - _rejestracja/sql/2026-04-24-registration-form-settings.sql + - _rejestracja/sql/apply-2026-04-24-registration-form-settings.php +autonomous: true +delegation: off +--- + + +## Goal +Add an administrator "Ustawienia formularza" page next to "Slowniki" and "Zmienne serwisu", and make registration day options plus every price used by the registration price calculation editable from that page. + +## Purpose +The registration form currently mixes editable `mf_parameters` prices with hardcoded one-day prices and hardcoded `participation_days` labels in the Smarty/JavaScript template. The client needs those operational values changeable at any time without editing source code. + +## Output +- New admin controller and template for form settings. +- Shared settings helper for reading defaults from `wp_setup` and `mf_parameters`. +- Public registration form and submit flow using the shared settings for day labels and pricing. +- SQL and PHP migration runner to seed default settings on production. + + + +## Project Context +@.paul/PROJECT.md +@.paul/ROADMAP.md +@.paul/STATE.md + +## Prior Work +@.paul/phases/01-registration-form-update/01-01-SUMMARY.md +@.paul/phases/02-registration-dictionary-phrases/02-01-SUMMARY.md + +## Source Files +@_rejestracja/controller/IndexController.php +@_rejestracja/template/partial/Index/Index.tpl +@_rejestracja/Admin/controller/SetupController.php +@_rejestracja/Admin/controller/DictionaryController.php +@_rejestracja/Admin/template/partial/Setup/Index.tpl +@_rejestracja/Admin/controller/CalcController.php +@_rejestracja/Admin/template/partial/Calc/Add.tpl +@_rejestracja/Admin/template/partial/Calc/Edit.tpl +@_rejestracja/core/_model/SetupDAL.class.php +@_rejestracja/core/_model/MfParameters.class.php +@_rejestracja/core/_model/MfParametersDAL.class.php + + + + +## AC-1: Admin Menu Entry +```gherkin +Given an administrator is on the Dictionary or Setup admin area +When the admin side menu renders +Then the menu includes "Ustawienia formularza" linking to /_rejestracja/Admin/FormSettings/Index next to "Slowniki" and "Zmienne serwisu" +``` + +## AC-2: Editable Participation Days +```gherkin +Given an administrator opens "Ustawienia formularza" +When they edit the one-day with lodging and one-day without lodging day labels +Then the public registration form renders those labels as participation_days radio options and submitted registrations store the selected configured label +``` + +## AC-3: Editable Calculation Prices +```gherkin +Given an administrator opens "Ustawienia formularza" +When they edit full conference prices, single-room/accompanying-person/English-conference surcharges, one-day prices, or VAT multiplier +Then calculatePrice on the public form and the server-side saved participant price use the edited values consistently +``` + +## AC-4: Deployment Seed +```gherkin +Given production has not yet created the new form settings variables +When the deployment SQL or PHP runner is applied +Then default settings are inserted without overwriting existing values and the form still shows the current three November day options and current prices +``` + + + + + + + Task 1: Add Shared Registration Settings Helper + _rejestracja/core/class/RegistrationFormSettings.class.php, _rejestracja/sql/2026-04-24-registration-form-settings.sql, _rejestracja/sql/apply-2026-04-24-registration-form-settings.php + + Create a `RegistrationFormSettings` helper that centralizes editable values: + - Read/write day labels from `wp_setup` variables using a clear prefix such as `registration_form_days_lodging` and `registration_form_days_no_lodging`; store list values as newline-separated text to keep the existing `wp_setup` table unchanged. + - Read/write the VAT multiplier from `wp_setup` as `registration_form_vat_multiplier`, defaulting to `1.23`. + - Read/write one-day net prices from `wp_setup` as `registration_form_one_day_price_prom` and `registration_form_one_day_price_normal`, defaulting to `1300` and `1600`. + - Read/write existing full conference and surcharge prices through `mf_parameters` rows, preserving IDs currently used by the form: `1` full conference, `2` single-room surcharge, `3` accompanying person/driver, `4` English conference, `5` legacy one-day. + - Return a normalized settings array for templates containing `days_lodging`, `days_no_lodging`, `prices`, and `vat_multiplier`. + - Provide save methods that normalize commas to dots for numbers, trim list entries, and leave unrelated `wp_setup` variables untouched. + Add SQL and PHP runner files that seed the new `wp_setup` variables only when missing. Do not change database schema. + + Run `php -l _rejestracja/core/class/RegistrationFormSettings.class.php` and `php -l _rejestracja/sql/apply-2026-04-24-registration-form-settings.php`. + AC-2, AC-3, and AC-4 have a shared source of truth and deployment defaults. + + + + Task 2: Build Admin Form Settings Page + _rejestracja/Admin/controller/FormSettingsController.php, _rejestracja/Admin/template/partial/FormSettings/Index.tpl, _rejestracja/Admin/controller/DictionaryController.php, _rejestracja/Admin/controller/SetupController.php + + Add `FormSettingsController` with `IndexAction` and a `preDispatch` matching the existing admin controller style. + - On GET, load settings through `RegistrationFormSettings` and assign them to Smarty. + - On POST, validate required numeric fields with the existing `Validator` style where practical, save through the helper, and show an admin info/error message without redirect loops. + - Template should group fields into participation days, main prices, surcharge prices, one-day prices, and VAT multiplier. + - Update the local admin menu arrays in `DictionaryController`, `SetupController`, and the new controller to include `Ustawienia formularza` => `FormSettings`/`Index`. + - Keep existing Dictionary and Setup behavior unchanged. + + Run `php -l _rejestracja/Admin/controller/FormSettingsController.php` and inspect the generated route URL from the same `Router::GenerateUrl('dictpig', ...)` pattern used by Dictionary/Setup. + AC-1, AC-2, and AC-3 are satisfied from the administrator side. + + + + Task 3: Use Settings in Public Form and Submit Pricing + _rejestracja/controller/IndexController.php, _rejestracja/template/partial/Index/Index.tpl + + Replace hardcoded form settings with data from `RegistrationFormSettings`. + - In `IndexController::IndexAction`, load settings once and assign JSON-safe arrays for `days_lodging`, `days_no_lodging`, `prices`, and `vat_multiplier`. + - Replace hardcoded day radio labels in `Index.tpl` with Smarty loops over configured day arrays, keeping `name="participation_days"` and existing option visibility behavior. + - Replace hardcoded JS one-day prices `1300/1600` and gross values `1599/1968` with the configured net prices and VAT multiplier; gross should be computed from net * VAT multiplier. + - Update server-side `$resultPrice` calculation so conference fee `5` uses configured one-day promotional/normal price instead of relying on `mf_parameters` ID 5 when the selected `participation_option` is `one_day_lodging` or `one_day_no_lodging`. + - Keep existing `mf_parameters` behavior for full conference and surcharges so saved registrations/admin summaries continue to resolve existing parameter rows. + - Preserve legacy fallback for missing POST values and for old session/display data. + + Run `php -l _rejestracja/controller/IndexController.php`, then manually inspect the rendered Smarty syntax for valid `{foreach}` blocks and JavaScript object output. + AC-2 and AC-3 are satisfied from the public form through persisted participant price. + + + + + + +## DO NOT CHANGE +- Existing `mf_participant` schema and participant status/payment semantics. +- Existing dictionary phrase keys created in Phase 2. +- Existing public form field names: `participation_days`, `participation_option`, `one_day_lodging`, `conference_fee`, and surcharge checkbox names. +- Existing Dictionary and Setup edit flows beyond adding the new menu link. + +## SCOPE LIMITS +- Do not redesign the admin UI; follow the current table/dropdown container style. +- Do not replace the legacy `mf_parameters` calculator module; expose the relevant rows in the new settings page and keep compatibility. +- Do not add new third-party dependencies. +- Do not alter compiled Smarty cache files under `temp/compile`. + + + + +Before declaring plan complete: +- [ ] `php -l _rejestracja/core/class/RegistrationFormSettings.class.php` +- [ ] `php -l _rejestracja/Admin/controller/FormSettingsController.php` +- [ ] `php -l _rejestracja/controller/IndexController.php` +- [ ] `php -l _rejestracja/sql/apply-2026-04-24-registration-form-settings.php` +- [ ] Confirm no hardcoded `3 listopada`, `4 listopada`, `5 listopada`, `3/4 listopada`, `4/5 listopada`, `1300`, `1600`, `1599`, or `1968` remain in active registration form pricing code except as migration/default seed values. +- [ ] Confirm the admin menu in Dictionary, Setup, and FormSettings includes the new "Ustawienia formularza" entry. + + + +- Administrators can open `/_rejestracja/Admin/FormSettings/Index`. +- The new page edits configured day options and all prices used by the current public registration calculation. +- Public JS price preview and server-side saved price use the same configured values. +- Deployment seed is idempotent and does not overwrite production edits. +- All verification checks pass. + + + +After completion, create `.paul/phases/03-registration-form-settings/03-01-SUMMARY.md` + diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json new file mode 100644 index 0000000..bb9a1ba --- /dev/null +++ b/.vscode/ftp-kr.sync.cache.json @@ -0,0 +1,454 @@ +{ + "ftp://aktualia.com.pl:21@aktualia": { + "www": { + ".htaccess": { + "type": "-", + "size": 726, + "lmtime": 0, + "modified": false + }, + "NAWA": {}, + "_rejestracja": { + ".htaccess": { + "type": "-", + "size": 571, + "lmtime": 0, + "modified": false + }, + ".htpasswd": { + "type": "-", + "size": 37, + "lmtime": 0, + "modified": false + }, + "Admin": { + "controller": { + "CalcController.php": { + "type": "-", + "size": 9144, + "lmtime": 1777037268858, + "modified": false + } + }, + "template": { + "partial": { + "Calc": { + "Add.tpl": { + "type": "-", + "size": 4064, + "lmtime": 0, + "modified": false + }, + "Edit.tpl": { + "type": "-", + "size": 4135, + "lmtime": 0, + "modified": false + }, + "Index.tpl": { + "type": "-", + "size": 2513, + "lmtime": 0, + "modified": false + }, + "Reg.tpl": { + "type": "-", + "size": 7316, + "lmtime": 1777058742417, + "modified": false + }, + "Reg.tpl.bak": { + "type": "-", + "size": 5077, + "lmtime": 0, + "modified": false + }, + "RegEdit.tpl": { + "type": "-", + "size": 7786, + "lmtime": 1777058742418, + "modified": false + }, + "RegPAN.tpl": { + "type": "-", + "size": 3909, + "lmtime": 0, + "modified": false + } + } + } + } + }, + "Server": {}, + "Static": {}, + "_Admin": {}, + "captcha.png": { + "type": "-", + "size": 159, + "lmtime": 0, + "modified": false + }, + "controller": { + "FileController.php": { + "type": "-", + "size": 1815, + "lmtime": 0, + "modified": false + }, + "IndexController.php": { + "type": "-", + "size": 24230, + "lmtime": 1777037695974, + "modified": false + }, + "ProductController.php": { + "type": "-", + "size": 4439, + "lmtime": 0, + "modified": false + }, + "SearchController.php": { + "type": "-", + "size": 2280, + "lmtime": 0, + "modified": false + }, + "SharedController.php": { + "type": "-", + "size": 37493, + "lmtime": 0, + "modified": false + }, + "SimpleArticle": {}, + "TranslateController.php": { + "type": "-", + "size": 961, + "lmtime": 0, + "modified": false + } + }, + "core": { + "model": { + "MfParticipant.class.php": { + "type": "-", + "size": 10686, + "lmtime": 1777036966082, + "modified": false + } + }, + "_model": { + "MfParticipant.class.php": { + "type": "-", + "size": 10492, + "lmtime": 1777036966083, + "modified": false + } + } + }, + "error.html": { + "type": "-", + "size": 55, + "lmtime": 0, + "modified": false + }, + "index.php": { + "type": "-", + "size": 1574, + "lmtime": 0, + "modified": false + }, + "info.php": { + "type": "-", + "size": 20, + "lmtime": 0, + "modified": false + }, + "logs": {}, + "makl.php": { + "type": "-", + "size": 350, + "lmtime": 0, + "modified": false + }, + "module": {}, + "readme.txt": { + "type": "-", + "size": 30, + "lmtime": 0, + "modified": false + }, + "routes.php": { + "type": "-", + "size": 1470, + "lmtime": 0, + "modified": false + }, + "tar.php": { + "type": "-", + "size": 359, + "lmtime": 0, + "modified": false + }, + "temp": {}, + "template": { + "partial": { + "Index": { + "AjaxForm.tpl": { + "type": "-", + "size": 3940, + "lmtime": 0, + "modified": false + }, + "Captcha.tpl": { + "type": "-", + "size": 0, + "lmtime": 0, + "modified": false + }, + "Index.tpl": { + "type": "-", + "size": 28715, + "lmtime": 1777058498780, + "modified": false + }, + "Index.tpl.bak": { + "type": "-", + "size": 20523, + "lmtime": 0, + "modified": false + }, + "IndexSent.tpl": { + "type": "-", + "size": 8558, + "lmtime": 1777058549495, + "modified": false + }, + "IndexSent_.tpl_": { + "type": "-", + "size": 6281, + "lmtime": 0, + "modified": false + }, + "IndexSent_good.tpl": { + "type": "-", + "size": 5874, + "lmtime": 0, + "modified": false + }, + "Index_1data.tpl_": { + "type": "-", + "size": 19972, + "lmtime": 0, + "modified": false + }, + "Index__.tp": { + "type": "-", + "size": 20332, + "lmtime": 0, + "modified": false + }, + "Index_good.tpl": { + "type": "-", + "size": 20143, + "lmtime": 0, + "modified": false + }, + "Index_pokonf.tp_": { + "type": "-", + "size": 20303, + "lmtime": 0, + "modified": false + }, + "MailTxt.tpl": { + "type": "-", + "size": 286, + "lmtime": 0, + "modified": false + }, + "Maps.tpl": { + "type": "-", + "size": 1737, + "lmtime": 0, + "modified": false + }, + "_": {} + } + } + }, + "zip.php": { + "type": "-", + "size": 856, + "lmtime": 0, + "modified": false + }, + "sql": { + "2026-04-24-registration-dictionary-phrases.sql": { + "type": "-", + "size": 13272, + "lmtime": 1777058742415, + "modified": false + }, + "2026-04-24-registration-form-update.sql": { + "type": "-", + "size": 624, + "lmtime": 1777037007411, + "modified": false + }, + "apply-2026-04-24-registration-dictionary-phrases.php": { + "type": "-", + "size": 7335, + "lmtime": 1777058742416, + "modified": false + }, + "apply-2026-04-24-registration-form-update.php": { + "type": "-", + "size": 3804, + "lmtime": 1777038005276, + "modified": false + } + } + }, + "ankieta.html": { + "type": "-", + "size": 5679, + "lmtime": 0, + "modified": false + }, + "bw": {}, + "css": {}, + "deklaracja_dostepnosci.html": { + "type": "-", + "size": 14085, + "lmtime": 0, + "modified": false + }, + "flexslider.css": { + "type": "-", + "size": 7141, + "lmtime": 0, + "modified": false + }, + "foto_2018": {}, + "foto_2019": {}, + "foto_2021": {}, + "foto_2022": {}, + "foto_2023": {}, + "foto_2025": {}, + "galeria.html": { + "type": "-", + "size": 5875, + "lmtime": 0, + "modified": false + }, + "galeria_2018.html": { + "type": "-", + "size": 15666, + "lmtime": 0, + "modified": false + }, + "galeria_2019.html": { + "type": "-", + "size": 10275, + "lmtime": 0, + "modified": false + }, + "galeria_2021.html": { + "type": "-", + "size": 12994, + "lmtime": 0, + "modified": false + }, + "galeria_2022.html": { + "type": "-", + "size": 11299, + "lmtime": 0, + "modified": false + }, + "galeria_2023.html": { + "type": "-", + "size": 13716, + "lmtime": 0, + "modified": false + }, + "galeria_2025.html": { + "type": "-", + "size": 15910, + "lmtime": 0, + "modified": false + }, + "images": {}, + "img": {}, + "index.html": { + "type": "-", + "size": 9600, + "lmtime": 0, + "modified": false + }, + "informacje.html": { + "type": "-", + "size": 9379, + "lmtime": 0, + "modified": false + }, + "jquery.flexslider.js": { + "type": "-", + "size": 54943, + "lmtime": 0, + "modified": false + }, + "js": {}, + "komitet_n.php": { + "type": "-", + "size": 3645, + "lmtime": 0, + "modified": false + }, + "kontakt.html": { + "type": "-", + "size": 6326, + "lmtime": 0, + "modified": false + }, + "materialy.html": { + "type": "-", + "size": 7896, + "lmtime": 0, + "modified": false + }, + "nowe": {}, + "okonferencji.html": { + "type": "-", + "size": 11546, + "lmtime": 0, + "modified": false + }, + "pliki": {}, + "program.html": { + "type": "-", + "size": 6058, + "lmtime": 0, + "modified": false + }, + "program_wstep.html": { + "type": "-", + "size": 12024, + "lmtime": 0, + "modified": false + }, + "rejestracja.html": { + "type": "-", + "size": 6354, + "lmtime": 0, + "modified": false + }, + "sponsorzy": {}, + "style.css": { + "type": "-", + "size": 21547, + "lmtime": 0, + "modified": false + } + } + }, + "$version": 1 +} \ No newline at end of file diff --git a/_rejestracja/Admin/controller/DictionaryController.php b/_rejestracja/Admin/controller/DictionaryController.php index 93b968f..f2b178d 100644 --- a/_rejestracja/Admin/controller/DictionaryController.php +++ b/_rejestracja/Admin/controller/DictionaryController.php @@ -108,6 +108,7 @@ class DictionaryController extends MainController implements ControllerInterface //'User' => array('User' => 'Index'), 'Słowniki' => array('Dictionary' => 'Index'), 'Zmienne serwisu' => array('Setup' => 'Index'), + 'Ustawienia formularza' => array('FormSettings' => 'Index'), // 'Kategorie produktów' => array('ProductCategory' => 'Index'), // 'Serie produktów' => array('ProductSeries' => 'Index'), // 'Materiał produktów' => array('ProductSpec' => 'Index', 'type' => 2), @@ -150,4 +151,4 @@ class DictionaryController extends MainController implements ControllerInterface -?> \ No newline at end of file +?> diff --git a/_rejestracja/Admin/controller/FormSettingsController.php b/_rejestracja/Admin/controller/FormSettingsController.php new file mode 100644 index 0000000..d6c71a9 --- /dev/null +++ b/_rejestracja/Admin/controller/FormSettingsController.php @@ -0,0 +1,61 @@ +smarty->assign('info', 'Zapisano ustawienia formularza.'); + $this->smarty->assign('type', 'ok'); + } else { + $this->smarty->assign('info', 'Popraw pola formularza.'); + $this->smarty->assign('type', 'error'); + } + } + + $settings = RegistrationFormSettings::GetSettings(); + $this->smarty->assign('settings', $settings); + $this->smarty->assign('errors', $errors); + } + + public function preDispatch($param) { + $this->RunShared('Auth', $param); + $this->Run($param); + $admin = AuthDAL::GetAdmin(); + $this->user = $admin; + + $this->smarty->assign('titleAdmin', 'Administracja'); + $struct = array( + 'Słowniki' => array('Dictionary' => 'Index'), + 'Zmienne serwisu' => array('Setup' => 'Index'), + 'Ustawienia formularza' => array('FormSettings' => 'Index'), + ); + + $this->smarty->assign('structure', $this->renderStruct($struct)); + } + + private function renderStruct($struct) { + $return = ''; + + foreach ($struct as $k => $row) { + $return .= '
  • ' . $k . '
  • '; + } + + $html = ''; + + return $html; + } + + public function postDispatch($param) { + } +} + +?> diff --git a/_rejestracja/Admin/controller/SetupController.php b/_rejestracja/Admin/controller/SetupController.php index 31182d8..5728b20 100644 --- a/_rejestracja/Admin/controller/SetupController.php +++ b/_rejestracja/Admin/controller/SetupController.php @@ -144,6 +144,7 @@ class SetupController extends MainController implements ControllerInterface { //'User' => array('User' => 'Index'), 'Słowniki' => array('Dictionary' => 'Index'), 'Zmienne serwisu' => array('Setup' => 'Index'), + 'Ustawienia formularza' => array('FormSettings' => 'Index'), // 'Kategorie produktów' => array('ProductCategory' => 'Index'), // 'Serie produktów' => array('ProductSeries' => 'Index'), // 'Materiał produktów' => array('ProductSpec' => 'Index', 'type' => 2), @@ -179,4 +180,4 @@ class SetupController extends MainController implements ControllerInterface { } } -?> \ No newline at end of file +?> diff --git a/_rejestracja/Admin/template/partial/FormSettings/Index.tpl b/_rejestracja/Admin/template/partial/FormSettings/Index.tpl new file mode 100644 index 0000000..3089a2d --- /dev/null +++ b/_rejestracja/Admin/template/partial/FormSettings/Index.tpl @@ -0,0 +1,72 @@ +
    +
    +
    +{dropDownContainer title="Ustawienia formularza"} +{if isset($info)} +
    {$info}
    +{/if} +
    + + + + + + + + + + + + + + + + + + + + + + +{foreach from=$settings.prices key=priceKey item=price} +{if $priceKey != 'one_day'} + + + + + +{/if} +{/foreach} + + + + + + + + + + + + + + + + + + + + + + + + + +
    Dni udzialu
    Jeden dzien z noclegiem{if isset($errors.registration_form_days_lodging)}{$errors.registration_form_days_lodging}{/if}
    Jeden dzien bez noclegu{if isset($errors.registration_form_days_no_lodging)}{$errors.registration_form_days_no_lodging}{/if}
    Ceny z parametrow formularza
    NazwaCena normalna nettoCena obnizona netto
    {$price.name} (ID {$price.id})
    Ceny jednego dnia
    Jeden dzien - cena obnizona netto{if isset($errors.registration_form_one_day_price_prom)}{$errors.registration_form_one_day_price_prom}{/if}
    Jeden dzien - cena normalna netto{if isset($errors.registration_form_one_day_price_normal)}{$errors.registration_form_one_day_price_normal}{/if}
    VAT
    Mnoznik VAT{if isset($errors.registration_form_vat_multiplier)}{$errors.registration_form_vat_multiplier}{/if}
    + +
    +
    +{/dropDownContainer} +
    +
    +
    diff --git a/_rejestracja/Static/image/Admin/Thumbs.db b/_rejestracja/Static/image/Admin/Thumbs.db deleted file mode 100644 index af5b082..0000000 Binary files a/_rejestracja/Static/image/Admin/Thumbs.db and /dev/null differ diff --git a/_rejestracja/controller/IndexController.php b/_rejestracja/controller/IndexController.php index f630b12..8386136 100644 --- a/_rejestracja/controller/IndexController.php +++ b/_rejestracja/controller/IndexController.php @@ -16,14 +16,19 @@ class IndexController extends MainController implements ControllerInterface { //Utils::ArrayDisplay($_POST); //SessionProxy::SetValue("infoSent",'true'); + $registrationSettings = RegistrationFormSettings::GetSettings(); + $vatMultiplier = $registrationSettings['vat_multiplier']; + $arrayPriceVat = array(); + $arrayPricePromVat = array(); + $arrayPrice = Utils::GetArrayList('mf_parameters', 'id_mf_parameters', 'price'); $arrayPriceProm = Utils::GetArrayList('mf_parameters', 'id_mf_parameters', 'price_prom'); foreach ($arrayPrice as $key => $price) { - $priceVat = $price*1.23; + $priceVat = $price*$vatMultiplier; $arrayPriceVat[$key] = $priceVat; } foreach ($arrayPriceProm as $key => $price) { - $priceVat = $price*1.23; + $priceVat = $price*$vatMultiplier; $arrayPricePromVat[$key] = $priceVat; } @@ -32,6 +37,9 @@ class IndexController extends MainController implements ControllerInterface { $this->smarty->assign('arrayPriceVatJson', str_replace('"', '', json_encode($arrayPriceVat))); $this->smarty->assign('arrayPricePromJson', str_replace('"', '', json_encode($arrayPriceProm))); $this->smarty->assign('arrayPricePromVatJson', json_encode($arrayPricePromVat)); + $this->smarty->assign('registrationSettings', $registrationSettings); + $this->smarty->assign('registrationPricesJson', json_encode($registrationSettings['prices'])); + $this->smarty->assign('registrationVatMultiplier', $vatMultiplier); $type = 1; @@ -213,11 +221,20 @@ class IndexController extends MainController implements ControllerInterface { $arrayPrice2Value = array(); $objParticipant->setFeeFull(serialize($arrayFee)); - if (isset($arrayFee['disc'])) { - unset($arrayFee['disc']); + $arrayFeeForCalculation = $arrayFee; + if (isset($arrayFeeForCalculation['disc'])) { + unset($arrayFeeForCalculation['disc']); + } + if (Request::GetPost('conference_fee') == 5) { + $resultPrice = RegistrationFormSettings::GetOneDayPrice(Request::GetPost('conference_fee_disc')); + foreach ($arrayFeeForCalculation as $feeKey => $feeValue) { + if ($feeValue == 5) { + unset($arrayFeeForCalculation[$feeKey]); + } + } } foreach ($arrayObjParameters as $objParam) { - if (in_array($objParam->GetId(), $arrayFee) != '') { + if (in_array($objParam->GetId(), $arrayFeeForCalculation) != '') { if (Request::GetPost('conference_fee_disc') == 2) { if($objParam->GetId() != 2 || $_POST['fee_room1'] != null){ $resultPrice = $resultPrice + $objParam->getPrice(); diff --git a/_rejestracja/core/class/RegistrationFormSettings.class.php b/_rejestracja/core/class/RegistrationFormSettings.class.php new file mode 100644 index 0000000..59c5b39 --- /dev/null +++ b/_rejestracja/core/class/RegistrationFormSettings.class.php @@ -0,0 +1,211 @@ + "3/4 listopada\n4/5 listopada", + 'registration_form_days_no_lodging' => "3 listopada\n4 listopada\n5 listopada", + 'registration_form_vat_multiplier' => '1.23', + 'registration_form_one_day_price_prom' => '1300', + 'registration_form_one_day_price_normal' => '1600', + ); + + private static $priceParameterIds = array( + 'full_conference' => 1, + 'single_room' => 2, + 'accompanying_person' => 3, + 'english_conference' => 4, + 'legacy_one_day' => 5, + ); + + public static function GetSettings() { + $variables = self::GetVariables(); + $vatMultiplier = self::NormalizeNumber(self::GetVariableValue($variables, self::VAT_MULTIPLIER)); + + if (!$vatMultiplier) { + $vatMultiplier = self::$defaults[self::VAT_MULTIPLIER]; + } + + return array( + 'days_lodging_text' => self::GetVariableValue($variables, self::DAYS_LODGING), + 'days_no_lodging_text' => self::GetVariableValue($variables, self::DAYS_NO_LODGING), + 'days_lodging' => self::ParseList(self::GetVariableValue($variables, self::DAYS_LODGING)), + 'days_no_lodging' => self::ParseList(self::GetVariableValue($variables, self::DAYS_NO_LODGING)), + 'vat_multiplier' => (float)$vatMultiplier, + 'one_day_price_prom' => (float)self::NormalizeNumber(self::GetVariableValue($variables, self::ONE_DAY_PRICE_PROM)), + 'one_day_price_normal' => (float)self::NormalizeNumber(self::GetVariableValue($variables, self::ONE_DAY_PRICE_NORMAL)), + 'prices' => self::GetPriceSettings((float)$vatMultiplier), + ); + } + + public static function SaveSettings($post) { + self::SaveVariable(self::DAYS_LODGING, self::NormalizeListText($post[self::DAYS_LODGING]), 'Dni udzialu - jeden dzien z noclegiem'); + self::SaveVariable(self::DAYS_NO_LODGING, self::NormalizeListText($post[self::DAYS_NO_LODGING]), 'Dni udzialu - jeden dzien bez noclegu'); + self::SaveVariable(self::VAT_MULTIPLIER, self::NormalizeNumber($post[self::VAT_MULTIPLIER]), 'Mnoznik VAT dla cen formularza rejestracji'); + self::SaveVariable(self::ONE_DAY_PRICE_PROM, self::NormalizeNumber($post[self::ONE_DAY_PRICE_PROM]), 'Cena netto promocyjna - jeden dzien'); + self::SaveVariable(self::ONE_DAY_PRICE_NORMAL, self::NormalizeNumber($post[self::ONE_DAY_PRICE_NORMAL]), 'Cena netto normalna - jeden dzien'); + + foreach (self::$priceParameterIds as $key => $id) { + if (!isset($post['price'][$id])) { + continue; + } + + $objParam = MfParametersDAL::GetById($id); + if (!$objParam || $objParam->GetId() == '-1') { + continue; + } + + $price = isset($post['price'][$id]) ? self::NormalizeNumber($post['price'][$id]) : ''; + $priceProm = isset($post['price_prom'][$id]) ? self::NormalizeNumber($post['price_prom'][$id]) : ''; + $objParam->setPrice($price); + $objParam->setPriceProm($priceProm); + MfParametersDAL::Save($objParam); + } + } + + public static function Validate($post) { + $errors = array(); + $required = array( + self::DAYS_LODGING => 'Podaj dni dla opcji z noclegiem.', + self::DAYS_NO_LODGING => 'Podaj dni dla opcji bez noclegu.', + self::VAT_MULTIPLIER => 'Podaj mnoznik VAT.', + self::ONE_DAY_PRICE_PROM => 'Podaj cene promocyjna za jeden dzien.', + self::ONE_DAY_PRICE_NORMAL => 'Podaj cene normalna za jeden dzien.', + ); + + foreach ($required as $field => $message) { + if (!isset($post[$field]) || trim($post[$field]) === '') { + $errors[$field] = $message; + } + } + + foreach (array(self::VAT_MULTIPLIER, self::ONE_DAY_PRICE_PROM, self::ONE_DAY_PRICE_NORMAL) as $field) { + if (isset($post[$field]) && trim($post[$field]) !== '' && !self::IsNumber($post[$field])) { + $errors[$field] = 'Pole musi byc liczba.'; + } + } + + if (isset($post['price']) && is_array($post['price'])) { + foreach ($post['price'] as $id => $value) { + if (trim($value) !== '' && !self::IsNumber($value)) { + $errors['price_' . $id] = 'Cena musi byc liczba.'; + } + } + } + + if (isset($post['price_prom']) && is_array($post['price_prom'])) { + foreach ($post['price_prom'] as $id => $value) { + if (trim($value) !== '' && !self::IsNumber($value)) { + $errors['price_prom_' . $id] = 'Cena promocyjna musi byc liczba.'; + } + } + } + + return $errors; + } + + public static function GetOneDayPrice($discountType) { + $settings = self::GetSettings(); + return $discountType == 2 ? $settings['one_day_price_normal'] : $settings['one_day_price_prom']; + } + + public static function GetVatMultiplier() { + $settings = self::GetSettings(); + return $settings['vat_multiplier']; + } + + private static function GetPriceSettings($vatMultiplier) { + $prices = array(); + + foreach (self::$priceParameterIds as $key => $id) { + $objParam = MfParametersDAL::GetById($id); + $net = $objParam && $objParam->GetId() != '-1' ? (float)$objParam->GetPrice() : 0; + $prom = $objParam && $objParam->GetId() != '-1' ? (float)$objParam->GetPriceProm() : 0; + + $prices[$key] = array( + 'id' => $id, + 'name' => $objParam && $objParam->GetId() != '-1' ? $objParam->GetName() : '', + 'price' => $net, + 'price_prom' => $prom, + 'price_vat' => $net * $vatMultiplier, + 'price_prom_vat' => $prom * $vatMultiplier, + ); + } + + $prices['one_day'] = array( + 'price_prom' => (float)self::GetVariableRaw(self::ONE_DAY_PRICE_PROM), + 'price_normal' => (float)self::GetVariableRaw(self::ONE_DAY_PRICE_NORMAL), + ); + + return $prices; + } + + private static function GetVariables() { + $variables = array(); + $db = Registry::Get('db'); + $stmt = $db->execute("select variable,value,description from wp_setup order by variable,value"); + $array = $stmt->FetchAllAssoc(); + + foreach ($array as $variable) { + $variables[$variable['variable']] = array( + 'variable' => $variable['variable'], + 'value' => $variable['value'], + 'description' => $variable['description'], + ); + } + + return $variables; + } + + private static function GetVariableValue($variables, $name) { + if (isset($variables[$name])) { + return $variables[$name]['value']; + } + + return self::$defaults[$name]; + } + + private static function GetVariableRaw($name) { + $variables = self::GetVariables(); + return self::NormalizeNumber(self::GetVariableValue($variables, $name)); + } + + private static function SaveVariable($name, $value, $description) { + SetupDAL::SaveVariable($name, $value, $description); + } + + private static function ParseList($text) { + $text = str_replace(array("\r\n", "\r"), "\n", $text); + $items = explode("\n", $text); + $return = array(); + + foreach ($items as $item) { + $item = trim($item); + if ($item !== '') { + $return[] = $item; + } + } + + return $return; + } + + private static function NormalizeListText($text) { + return implode("\n", self::ParseList($text)); + } + + private static function NormalizeNumber($value) { + return str_replace(',', '.', trim($value)); + } + + private static function IsNumber($value) { + return is_numeric(self::NormalizeNumber($value)); + } +} + +?> diff --git a/_rejestracja/sql/2026-04-24-registration-form-settings.sql b/_rejestracja/sql/2026-04-24-registration-form-settings.sql new file mode 100644 index 0000000..da3bff1 --- /dev/null +++ b/_rejestracja/sql/2026-04-24-registration-form-settings.sql @@ -0,0 +1,22 @@ +INSERT INTO wp_setup (`variable`, `value`, `description`) +SELECT 'registration_form_days_lodging', '3/4 listopada +4/5 listopada', 'Dni udzialu - jeden dzien z noclegiem' +WHERE NOT EXISTS (SELECT 1 FROM wp_setup WHERE `variable` = 'registration_form_days_lodging'); + +INSERT INTO wp_setup (`variable`, `value`, `description`) +SELECT 'registration_form_days_no_lodging', '3 listopada +4 listopada +5 listopada', 'Dni udzialu - jeden dzien bez noclegu' +WHERE NOT EXISTS (SELECT 1 FROM wp_setup WHERE `variable` = 'registration_form_days_no_lodging'); + +INSERT INTO wp_setup (`variable`, `value`, `description`) +SELECT 'registration_form_vat_multiplier', '1.23', 'Mnoznik VAT dla cen formularza rejestracji' +WHERE NOT EXISTS (SELECT 1 FROM wp_setup WHERE `variable` = 'registration_form_vat_multiplier'); + +INSERT INTO wp_setup (`variable`, `value`, `description`) +SELECT 'registration_form_one_day_price_prom', '1300', 'Cena netto promocyjna - jeden dzien' +WHERE NOT EXISTS (SELECT 1 FROM wp_setup WHERE `variable` = 'registration_form_one_day_price_prom'); + +INSERT INTO wp_setup (`variable`, `value`, `description`) +SELECT 'registration_form_one_day_price_normal', '1600', 'Cena netto normalna - jeden dzien' +WHERE NOT EXISTS (SELECT 1 FROM wp_setup WHERE `variable` = 'registration_form_one_day_price_normal'); diff --git a/_rejestracja/sql/apply-2026-04-24-registration-form-settings.php b/_rejestracja/sql/apply-2026-04-24-registration-form-settings.php new file mode 100644 index 0000000..85c775e --- /dev/null +++ b/_rejestracja/sql/apply-2026-04-24-registration-form-settings.php @@ -0,0 +1,145 @@ + array( + 'value' => "3/4 listopada\n4/5 listopada", + 'description' => 'Dni udzialu - jeden dzien z noclegiem', + ), + 'registration_form_days_no_lodging' => array( + 'value' => "3 listopada\n4 listopada\n5 listopada", + 'description' => 'Dni udzialu - jeden dzien bez noclegu', + ), + 'registration_form_vat_multiplier' => array( + 'value' => '1.23', + 'description' => 'Mnoznik VAT dla cen formularza rejestracji', + ), + 'registration_form_one_day_price_prom' => array( + 'value' => '1300', + 'description' => 'Cena netto promocyjna - jeden dzien', + ), + 'registration_form_one_day_price_normal' => array( + 'value' => '1600', + 'description' => 'Cena netto normalna - jeden dzien', + ), +); + +$isCli = PHP_SAPI === 'cli'; +$approved = $isCli + ? in_array('--run', $argv, true) + : (isset($_GET['run']) && $_GET['run'] === '20260424'); + +header_safe('Content-Type: text/plain; charset=utf-8'); + +if (!$approved) { + echo "DRY RUN ONLY\n"; + echo "To apply form settings seed, run with ?run=20260424 in browser or --run in CLI.\n"; + echo "No database changes were made.\n"; + exit; +} + +$configPath = __DIR__ . '/../core/config/Strona/db.config.ini'; +if (!is_file($configPath)) { + fail("Config file not found: " . $configPath); +} + +$config = parse_ini_file($configPath, true); +if (!isset($config['db'])) { + fail("Missing [db] section in config."); +} + +$dbConfig = $config['db']; +foreach (array('prodHost', 'prodUser', 'prodPass', 'prodDb') as $key) { + if (!array_key_exists($key, $dbConfig)) { + fail("Missing db config key: " . $key); + } +} + +$mysqli = new mysqli( + $dbConfig['prodHost'], + $dbConfig['prodUser'], + $dbConfig['prodPass'], + $dbConfig['prodDb'] +); + +if ($mysqli->connect_errno) { + fail("MySQL connection failed: " . $mysqli->connect_error); +} + +$mysqli->set_charset('utf8'); + +echo "Applying registration form settings seed...\n"; + +$inserted = 0; +$skipped = 0; + +foreach ($settings as $variable => $row) { + if (setup_variable_exists($mysqli, $variable)) { + $skipped++; + continue; + } + + insert_setup_variable($mysqli, $variable, $row['value'], $row['description']); + $inserted++; +} + +echo "Form settings seed complete.\n"; +echo "Inserted: " . $inserted . "\n"; +echo "Skipped: " . $skipped . "\n"; +$mysqli->close(); + +function setup_variable_exists(mysqli $mysqli, $variable) { + $stmt = $mysqli->prepare("SELECT COUNT(*) AS cnt FROM wp_setup WHERE variable = ?"); + if (!$stmt) { + fail("Prepare failed: " . $mysqli->error); + } + + $stmt->bind_param('s', $variable); + if (!$stmt->execute()) { + fail("Setup check failed: " . $stmt->error); + } + + $result = $stmt->get_result(); + $row = $result->fetch_assoc(); + $stmt->close(); + + return isset($row['cnt']) && (int)$row['cnt'] > 0; +} + +function insert_setup_variable(mysqli $mysqli, $variable, $value, $description) { + $stmt = $mysqli->prepare("INSERT INTO wp_setup (variable, value, description) VALUES (?, ?, ?)"); + if (!$stmt) { + fail("Prepare failed: " . $mysqli->error); + } + + $stmt->bind_param('sss', $variable, $value, $description); + if (!$stmt->execute()) { + fail("Insert failed for " . $variable . ": " . $stmt->error); + } + + $stmt->close(); +} + +function fail($message) { + echo "ERROR: " . $message . "\n"; + exit(1); +} + +function header_safe($header) { + if (!headers_sent() && PHP_SAPI !== 'cli') { + header($header); + } +} + +?> diff --git a/_rejestracja/template/partial/Index/Index.tpl b/_rejestracja/template/partial/Index/Index.tpl index 067ca75..1821e34 100644 --- a/_rejestracja/template/partial/Index/Index.tpl +++ b/_rejestracja/template/partial/Index/Index.tpl @@ -72,7 +72,10 @@
    {translate word='NIP Instytucji'}:
    -
    {formField name="nip" type="text" errorClass="warning"}
    +
    + {formField name="nip" type="text" errorClass="warning"} +
    {translate word='Do wypełnienia w przypadku posiadania identyfikatora wewnętrznego w KSEF i prośba o podanie KSeF ID wew.'}
    +
    @@ -178,32 +181,27 @@