2026-05-18 07:48:26 +00:00
|
|
|
menu.dashboard: Dashboard
|
|
|
|
|
menu.ingest_article: 'Ingest Article'
|
|
|
|
|
menu.articles: Articles
|
|
|
|
|
menu.article_types: 'Article Types'
|
|
|
|
|
menu.attributes: Attributes
|
|
|
|
|
menu.section_pipeline: Pipeline
|
|
|
|
|
menu.active_jobs: 'Active Jobs'
|
|
|
|
|
menu.archive: Archive
|
|
|
|
|
menu.ai_prompts: 'AI Prompts'
|
2026-05-18 07:53:52 +00:00
|
|
|
menu.translations: Translations
|
2026-05-18 07:48:26 +00:00
|
|
|
menu.users: Users
|
|
|
|
|
menu.logs: Logs
|
2026-05-19 07:13:51 +00:00
|
|
|
menu.section_ebay: eBay
|
|
|
|
|
menu.ebay_platform_configs: 'Category Configurations'
|
2026-05-18 07:48:26 +00:00
|
|
|
menu.section_sales: Sales
|
|
|
|
|
menu.orders: Orders
|
|
|
|
|
menu.customers: Customers
|
|
|
|
|
menu.invoices: Invoices
|
|
|
|
|
menu.change_password: 'Change Password'
|
|
|
|
|
menu.language_en: English
|
|
|
|
|
menu.language_de: Deutsch
|
|
|
|
|
crud.article.singular: Article
|
|
|
|
|
crud.article.plural: Articles
|
|
|
|
|
crud.article_type.singular: 'Article Type'
|
|
|
|
|
crud.article_type.plural: 'Article Types'
|
|
|
|
|
crud.attribute.singular: Attribute
|
|
|
|
|
crud.attribute.plural: Attributes
|
|
|
|
|
crud.attribute_assignment.singular: 'Attribute Assignment'
|
|
|
|
|
crud.attribute_assignment.plural: 'Attribute Assignments'
|
|
|
|
|
crud.customer.singular: Customer
|
|
|
|
|
crud.customer.plural: Customers
|
|
|
|
|
crud.order.singular: Order
|
|
|
|
|
crud.order.plural: Orders
|
|
|
|
|
crud.invoice.singular: Invoice
|
|
|
|
|
crud.invoice.plural: Invoices
|
|
|
|
|
crud.log_entry.singular: 'Log Entry'
|
|
|
|
|
crud.log_entry.plural: 'Log Entries'
|
|
|
|
|
crud.user.singular: User
|
|
|
|
|
crud.user.plural: Users
|
|
|
|
|
crud.pipeline_job.singular: 'Pipeline Job'
|
|
|
|
|
crud.pipeline_job.plural_active: 'AI Pipeline — Active'
|
|
|
|
|
crud.pipeline_job.plural_archive: 'AI Pipeline — Archive'
|
|
|
|
|
crud.prompt_template.singular: 'Prompt Template'
|
|
|
|
|
crud.prompt_template.plural: 'Prompt Templates'
|
2026-05-18 07:53:52 +00:00
|
|
|
crud.translation.singular: Translation
|
|
|
|
|
crud.translation.plural: Translations
|
2026-05-18 07:48:26 +00:00
|
|
|
field.id: ID
|
|
|
|
|
field.name: Name
|
|
|
|
|
field.type: Type
|
|
|
|
|
field.unit: Unit
|
|
|
|
|
field.options: 'Options (one per line)'
|
|
|
|
|
field.options_help: 'Only relevant for type select / multi_select.'
|
|
|
|
|
field.required_attributes: 'Required Attributes'
|
|
|
|
|
field.optional_attributes: 'Optional Attributes'
|
|
|
|
|
field.inventory_number: 'Inventory #'
|
|
|
|
|
field.status: Status
|
|
|
|
|
field.step: Step
|
|
|
|
|
field.attempts: Attempts
|
|
|
|
|
field.started: Started
|
|
|
|
|
field.error: Error
|
|
|
|
|
field.ai_results: 'AI Results'
|
feat: Frappe ERP matching, pipeline model cache, ACL, stock field, specs by type
Frappe ERP:
- findExistingCustomer() on FrappeErpAdapter — two-step name+address lookup
- FrappeHttpClient: add put() method; switch invoice submit to PUT docstatus=1 (Frappe v16)
- buildItemDescription() uses specsText + inventory number + serial number
- Integration tests: find Simon Kühn, create real 1337€ invoice, cancel+delete in tearDown
- FRAPPE_GENERIC_ITEM_CODE=SKU002 added to .env.local and bin/test-integration
Pipeline — model cache:
- PhotoUploadHandler: after vision, check DB for existing article with same modelNumber
- On match: copy ebayTitle/ebayDescription/specsText/attributes, skip specs+JSON+eBay steps
- DraftArticleHandler: apply model_match data and mark job complete directly
- ArticleRepository: findCompletedByModelNumber() query
Pipeline — specs by article type:
- SpecsResearchAgent: accept attributeFields list, format as bullet list in {{fields}} var
- SpecsResearchHandler: derive attribute names from ArticleType, pass to agent
- SpecsResearchMessage: add attributeFields param
- Prompt migration: replace hardcoded laptop spec list with {{fields}} placeholder
Article:
- specsText field (nullable text column + migration)
- stock field visible on index and editable in CRUD form
- addAttributeValue()/removeAttributeValue() adder-remover pair for Symfony form binding
- AttributeValue::getArticle() getter
- AttributeValueFormType: detect required attributes from ArticleType assignments, set required=true
- ManualIngestType: add stock/quantity field (default 1, min 1)
Users / ACL:
- PermissionVoter: define named permission constants + allPermissions()
- User: getGrantedPermissions()/setGrantedPermissions() helpers
- UserCrudController: permissions checkbox group on edit form
UI / assets:
- public/css/admin/custom.css: red asterisk for required fields
- DashboardController: register custom CSS
Infra:
- PipelineJobFailureListener: mark job failed (with real error) when Messenger exhausts retries
- doctrine.yaml: exclude app.inventory_seq from schema diff
- ErpAdapterInterface: add findExistingCustomer()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:42:15 +00:00
|
|
|
field.stock: Stock
|
2026-05-18 07:48:26 +00:00
|
|
|
field.price: Price
|
|
|
|
|
field.condition: Condition
|
|
|
|
|
field.manufacturer: Manufacturer
|
|
|
|
|
field.model_number: 'Model #'
|
2026-05-18 10:06:36 +00:00
|
|
|
field.model_name: 'Model Name'
|
2026-05-18 07:48:26 +00:00
|
|
|
field.serial_number: 'Serial #'
|
|
|
|
|
field.condition_notes: 'Condition Notes'
|
|
|
|
|
field.description: Description
|
|
|
|
|
field.ebay_description: 'eBay Description'
|
|
|
|
|
field.attributes: Attributes
|
|
|
|
|
field.prompt_key: Key
|
|
|
|
|
field.prompt_key_help: 'Slug identifying the prompt (e.g. <code>specs_research</code>). Must be unique.'
|
|
|
|
|
field.prompt_body: 'Prompt Body'
|
|
|
|
|
field.prompt_body_preview: 'Body Preview'
|
|
|
|
|
field.last_updated: 'Last Updated'
|
2026-05-18 09:10:59 +00:00
|
|
|
field.photos: Photos
|
2026-05-18 07:53:52 +00:00
|
|
|
field.locale: Locale
|
|
|
|
|
field.domain: Domain
|
|
|
|
|
field.translation_key: Key
|
|
|
|
|
field.translation_value: Value
|
2026-05-18 11:02:46 +00:00
|
|
|
action.delete_attribute_confirm: 'Delete this attribute? All values stored on existing articles will also be deleted.'
|
2026-05-18 07:48:26 +00:00
|
|
|
action.retry: Retry
|
|
|
|
|
action.retry_confirm: 'Re-queue this pipeline job from the current step?'
|
|
|
|
|
action.rerun_ai: 'Re-run AI'
|
|
|
|
|
action.rerun_ai_confirm: 'Re-run the full AI pipeline for this article? This will overwrite existing attributes and eBay texts.'
|
|
|
|
|
action.mark_as_draft: 'Mark as Draft'
|
|
|
|
|
action.activate: Activate
|
|
|
|
|
flash.pipeline_job_not_found: 'No original pipeline job found — cannot determine which photo to use.'
|
|
|
|
|
flash.photo_not_found: 'Stored photo not found at: %path%'
|
|
|
|
|
flash.pipeline_requeued: 'AI pipeline re-queued for %label% — attributes and eBay texts will be updated when complete.'
|
|
|
|
|
flash.article_marked_draft: 'Article marked as draft.'
|
|
|
|
|
flash.article_missing_attributes: 'Cannot activate: missing attributes: %attrs%'
|
|
|
|
|
flash.article_activated: 'Article activated and queued for channel publishing.'
|
|
|
|
|
flash.job_requeued: 'Job %id% re-queued from %step%.'
|
|
|
|
|
pipeline.step.vision: 'Photo analyzed'
|
|
|
|
|
pipeline.step.specs_research: 'Specs researched'
|
|
|
|
|
pipeline.step.json_coding: 'Attributes coded'
|
|
|
|
|
pipeline.step.draft_article: 'Article created'
|
|
|
|
|
pipeline.step.ebay_text: 'eBay texts generated'
|
|
|
|
|
pipeline.step.validation: Validation
|
|
|
|
|
pipeline.event.queued: 'Job #%inv% added to pipeline'
|
|
|
|
|
pipeline.event.processing_start: 'Job #%inv% started'
|
|
|
|
|
pipeline.event.processing_step: 'Job #%inv%: %step%'
|
|
|
|
|
pipeline.event.completed: 'Job #%inv% completed ✓'
|
|
|
|
|
pipeline.event.failed: 'Job #%inv% failed: %reason%'
|
|
|
|
|
pipeline.event.needs_review: 'Job #%inv% requires manual review'
|