This commit is contained in:
seb
2026-10-04 21:22:00 +02:00
parent 6777f423d8
commit 45369d11da
16 changed files with 133 additions and 61 deletions

View File

@@ -48,13 +48,13 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
| [`routes/article/_sku/children.put.md`](routes/article/_sku/children.put.md) | `PUT /article/{sku}/children` |
| [`routes/article/_sku/children/_childSku.md`](routes/article/_sku/children/_childSku.md) | `DELETE /article/{sku}/children/{childSku}` |
| [`routes/article/set-eans.md`](routes/article/set-eans.md) | `PUT /article/{sku}/eans` |
| [`routes/article/_sku/father.md`](routes/article/_sku/father.md) | `POST /article/{sku}/father` |
| [`routes/article/history.md`](routes/article/history.md) | `GET /article/{sku}/history` |
| [`routes/article/add-image.md`](routes/article/add-image.md) | `POST /article/{sku}/image` |
| [`routes/article/delete-image.md`](routes/article/delete-image.md) | `DELETE /article/{sku}/image/{fullHash}` |
| [`routes/article/order-images.md`](routes/article/order-images.md) | `PUT /article/{sku}/images/order` |
| [`routes/article/_sku/optionslist.get.md`](routes/article/_sku/optionslist.get.md) | `GET /article/{sku}/optionslist` |
| [`routes/article/_sku/optionslist.put.md`](routes/article/_sku/optionslist.put.md) | `PUT /article/{sku}/optionslist` |
| [`routes/article/_sku/parent.md`](routes/article/_sku/parent.md) | `POST /article/{sku}/parent` |
| [`routes/article/set-prices.md`](routes/article/set-prices.md) | `PUT /article/{sku}/prices` |
| [`routes/article/_sku/print.md`](routes/article/_sku/print.md) | `POST /article/{sku}/print` |
| [`routes/article/_sku/property.get.md`](routes/article/_sku/property.get.md) | `GET /article/{sku}/property` |

View File

@@ -47,24 +47,21 @@ Overview of all 181 API endpoints across all modules.
#### Replace article categories by SKU — `PUT /article/{sku}/categories`
- Params: `sku: string, body: { categoryIds: integer[] }`
- Returns: `200 { article: { id: integer, sku: string, name: string, slug: string, track_stock: boolean, unitamount: number, weight: number, unit: object, brand: object, categoryIds: integer[], photos: { sort: integer, fullHash: string, fullW: integer, fullH: integer, fullSize: integer, thumbW: integer, thumbH: integer, thumbHash: string }[], eans: string[], prices: { minmultiplier: number, net: number, taxRate: number, customergroupId: integer }[], suppliers: { id: integer, articleId: integer, entityId: integer, supplierName: string, sku: string, pu: number, stock: number, sort: integer, priceId: integer, price: { net: number, taxId: integer, taxRate: number } }[] } }`
#### List child articles of a father article — `GET /article/{sku}/children`
#### List child articles of a parent article — `GET /article/{sku}/children`
- Params: `sku: string`
- Returns: `200 { children: { id: integer, sku: string, name: string, fathersort: integer, attributeValueId: integer, attributeValue: string }[] }`
#### Create and attach a new child article to a father article — `POST /article/{sku}/children`
- Returns: `200 { children: { id: integer, sku: string, name: string, parentsort: integer, attributeValueId: integer, attributeValue: string }[] }`
#### Create and attach a new child article to a parent article — `POST /article/{sku}/children`
- Params: `sku: string, body: { attributeValueId?: integer, value?: string, name?: string }`
- Returns: `201 { article: { id: integer, categoryId: integer, name: string, sku: string, track_stock: boolean } }`
#### Attach an existing article as a child to a father article — `PUT /article/{sku}/children`
#### Attach an existing article as a child to a parent article — `PUT /article/{sku}/children`
- Params: `sku: string, body: { sku: string, attributeValueId?: integer, value?: string, reparent?: boolean }`
- Returns: `200 { article: { id: integer, categoryId: integer, name: string, sku: string, track_stock: boolean }, children: object[] }`
#### Remove a child article from a father article — `DELETE /article/{sku}/children/{childSku}`
#### Remove a child article from a parent article — `DELETE /article/{sku}/children/{childSku}`
- Params: `sku: string, childSku: string, query: deleteArticle?: boolean`
- Returns: `200 { success: boolean, deleted: boolean }`
#### Replace article EANs by SKU — `PUT /article/{sku}/eans`
- Params: `sku: string, body: { eans: string[] }`
- Returns: `200 { article: { id: integer, sku: string, name: string, slug: string, track_stock: boolean, unitamount: number, weight: number, unit: object, brand: object, categoryIds: integer[], photos: { sort: integer, fullHash: string, fullW: integer, fullH: integer, fullSize: integer, thumbW: integer, thumbH: integer, thumbHash: string }[], eans: string[], prices: { minmultiplier: number, net: number, taxRate: number, customergroupId: integer }[], suppliers: { id: integer, articleId: integer, entityId: integer, supplierName: string, sku: string, pu: number, stock: number, sort: integer, priceId: integer, price: { net: number, taxId: integer, taxRate: number } }[] } }`
#### Toggle father mode for an article — `POST /article/{sku}/father`
- Params: `sku: string, body: { isFather: boolean, attributeNameId?: integer }`
- Returns: `200 { article: { id: integer, categoryId: integer, name: string, sku: string, track_stock: boolean } }`
#### Get article history by SKU — `GET /article/{sku}/history`
- Params: `sku: string, query: limit?: integer`
- Returns: `200 { history: { id: integer, date: string, text: string }[], truncated: boolean }`
@@ -83,6 +80,9 @@ Overview of all 181 API endpoints across all modules.
#### Set options list for an article — `PUT /article/{sku}/optionslist`
- Params: `sku: string, body: { options: { articleId?: integer, sku?: string, count?: integer, sort?: integer, mandated?: boolean }[] }`
- Returns: `200 { options: object[] }`
#### Toggle parent mode for an article — `POST /article/{sku}/parent`
- Params: `sku: string, body: { isParent: boolean, attributeNameId?: integer }`
- Returns: `200 { article: { id: integer, categoryId: integer, name: string, sku: string, track_stock: boolean } }`
#### Replace POS prices by SKU — `PUT /article/{sku}/prices`
- Params: `sku: string, body: { prices: { price: number, tax: number, minmultiplier?: number }[] }`
- Returns: `200 { article: { id: integer, sku: string, name: string, slug: string, track_stock: boolean, unitamount: number, weight: number, unit: object, brand: object, categoryIds: integer[], photos: { sort: integer, fullHash: string, fullW: integer, fullH: integer, fullSize: integer, thumbW: integer, thumbH: integer, thumbHash: string }[], eans: string[], prices: { minmultiplier: number, net: number, taxRate: number, customergroupId: integer }[], suppliers: { id: integer, articleId: integer, entityId: integer, supplierName: string, sku: string, pu: number, stock: number, sort: integer, priceId: integer, price: { net: number, taxId: integer, taxRate: number } }[] } }`
@@ -463,7 +463,7 @@ _none_
- Returns: `200 { articles: { id: integer, sku: string, name: string, slug: string, net: number, taxRate: number, gross: number, trackStock: boolean, brand: { id: integer, name: string }, fullHash: string, thumbW: integer, thumbH: integer, thumbHash: string }[] }`
#### Get public article detail — `GET /public/{companyId}/articles/{sku}`
- Params: `companyId: integer, sku: string`
- Returns: `200 { article: { id: integer, sku: string, name: string, slug: string, description: string, unitamount: number, weight: number, trackStock: boolean, unit: object, brand: object, price: object, prices: object[], categories: object[], photos: object[], eans: string[], isFather: boolean, fatherSku: string, variants: object[] } }`
- Returns: `200 { article: { id: integer, sku: string, name: string, slug: string, description: string, unitamount: number, weight: number, trackStock: boolean, unit: object, brand: object, price: object, prices: object[], categories: object[], photos: object[], eans: string[], isParent: boolean, parentSku: string, variants: object[] } }`
#### List public categories — `GET /public/{companyId}/categories`
- Params: `companyId: integer`
- Returns: `200 { categories: { id: integer, parentId: integer, sort: integer, name: string, slug: string, fullHash: string, thumbW: integer, thumbH: integer, thumbHash: string }[] }`

View File

@@ -44,24 +44,21 @@ Overview of all 145 API endpoints across all modules.
#### Replace article categories by SKU — `PUT /article/{sku}/categories`
- Params: `sku: string, body: { categoryIds: integer[] }`
- Returns: `200 { article: { id: integer, sku: string, name: string, slug: string, track_stock: boolean, unitamount: number, weight: number, unit: object, brand: object, categoryIds: integer[], photos: { sort: integer, fullHash: string, fullW: integer, fullH: integer, fullSize: integer, thumbW: integer, thumbH: integer, thumbHash: string }[], eans: string[], prices: { minmultiplier: number, net: number, taxRate: number, customergroupId: integer }[], suppliers: { id: integer, articleId: integer, entityId: integer, supplierName: string, sku: string, pu: number, stock: number, sort: integer, priceId: integer, price: { net: number, taxId: integer, taxRate: number } }[] } }`
#### List child articles of a father article — `GET /article/{sku}/children`
#### List child articles of a parent article — `GET /article/{sku}/children`
- Params: `sku: string`
- Returns: `200 { children: { id: integer, sku: string, name: string, fathersort: integer, attributeValueId: integer, attributeValue: string }[] }`
#### Create and attach a new child article to a father article — `POST /article/{sku}/children`
- Returns: `200 { children: { id: integer, sku: string, name: string, parentsort: integer, attributeValueId: integer, attributeValue: string }[] }`
#### Create and attach a new child article to a parent article — `POST /article/{sku}/children`
- Params: `sku: string, body: { attributeValueId?: integer, value?: string, name?: string }`
- Returns: `201 { article: { id: integer, categoryId: integer, name: string, sku: string, track_stock: boolean } }`
#### Attach an existing article as a child to a father article — `PUT /article/{sku}/children`
#### Attach an existing article as a child to a parent article — `PUT /article/{sku}/children`
- Params: `sku: string, body: { sku: string, attributeValueId?: integer, value?: string, reparent?: boolean }`
- Returns: `200 { article: { id: integer, categoryId: integer, name: string, sku: string, track_stock: boolean }, children: object[] }`
#### Remove a child article from a father article — `DELETE /article/{sku}/children/{childSku}`
#### Remove a child article from a parent article — `DELETE /article/{sku}/children/{childSku}`
- Params: `sku: string, childSku: string, query: deleteArticle?: boolean`
- Returns: `200 { success: boolean, deleted: boolean }`
#### Replace article EANs by SKU — `PUT /article/{sku}/eans`
- Params: `sku: string, body: { eans: string[] }`
- Returns: `200 { article: { id: integer, sku: string, name: string, slug: string, track_stock: boolean, unitamount: number, weight: number, unit: object, brand: object, categoryIds: integer[], photos: { sort: integer, fullHash: string, fullW: integer, fullH: integer, fullSize: integer, thumbW: integer, thumbH: integer, thumbHash: string }[], eans: string[], prices: { minmultiplier: number, net: number, taxRate: number, customergroupId: integer }[], suppliers: { id: integer, articleId: integer, entityId: integer, supplierName: string, sku: string, pu: number, stock: number, sort: integer, priceId: integer, price: { net: number, taxId: integer, taxRate: number } }[] } }`
#### Toggle father mode for an article — `POST /article/{sku}/father`
- Params: `sku: string, body: { isFather: boolean, attributeNameId?: integer }`
- Returns: `200 { article: { id: integer, categoryId: integer, name: string, sku: string, track_stock: boolean } }`
#### Get article history by SKU — `GET /article/{sku}/history`
- Params: `sku: string, query: limit?: integer`
- Returns: `200 { history: { id: integer, date: string, text: string }[], truncated: boolean }`
@@ -77,6 +74,9 @@ Overview of all 145 API endpoints across all modules.
#### Set options list for an article — `PUT /article/{sku}/optionslist`
- Params: `sku: string, body: { options: { articleId?: integer, sku?: string, count?: integer, sort?: integer, mandated?: boolean }[] }`
- Returns: `200 { options: object[] }`
#### Toggle parent mode for an article — `POST /article/{sku}/parent`
- Params: `sku: string, body: { isParent: boolean, attributeNameId?: integer }`
- Returns: `200 { article: { id: integer, categoryId: integer, name: string, sku: string, track_stock: boolean } }`
#### Replace POS prices by SKU — `PUT /article/{sku}/prices`
- Params: `sku: string, body: { prices: { price: number, tax: number, minmultiplier?: number }[] }`
- Returns: `200 { article: { id: integer, sku: string, name: string, slug: string, track_stock: boolean, unitamount: number, weight: number, unit: object, brand: object, categoryIds: integer[], photos: { sort: integer, fullHash: string, fullW: integer, fullH: integer, fullSize: integer, thumbW: integer, thumbH: integer, thumbHash: string }[], eans: string[], prices: { minmultiplier: number, net: number, taxRate: number, customergroupId: integer }[], suppliers: { id: integer, articleId: integer, entityId: integer, supplierName: string, sku: string, pu: number, stock: number, sort: integer, priceId: integer, price: { net: number, taxId: integer, taxRate: number } }[] } }`

View File

@@ -4,7 +4,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
Set Stückliste (bundle items) for an article
Configures the article as a Stückliste covering a list of count * article. A father article cannot be a Stückliste. Can be combined with options. Pass an empty array to remove the Stückliste.
Configures the article as a Stückliste covering a list of count * article. A parent article cannot be a Stückliste. Can be combined with options. Pass an empty array to remove the Stückliste.
## Path parameters
@@ -51,7 +51,7 @@ Updated Stückliste items
| Status | Description |
|--------|-------------|
| 400 | Article is a father, has options, or contains itself |
| 400 | Article is a parent, has options, or contains itself |
| 404 | Article or component article not found |
---

View File

@@ -2,15 +2,15 @@
Protected. Requires a valid session cookie (`credentials: 'include'`).
List child articles of a father article
List child articles of a parent article
Returns variants/children linked to this father article.
Returns variants/children linked to this parent article.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `sku` | yes | string | Father article SKU |
| `sku` | yes | string | Parent article SKU |
## Response `200`
@@ -23,7 +23,7 @@ List of children
"id": 1,
"sku": "…",
"name": "…",
"fathersort": 1,
"parentsort": 1,
"attributeValueId": 1,
"attributeValue": "…"
}

View File

@@ -2,22 +2,22 @@
Protected. Requires a valid session cookie (`credentials: 'include'`).
Create and attach a new child article to a father article
Create and attach a new child article to a parent article
Automatically generates SKU as fatherSku-nextFreeIndex and name from father name + value. Rejects a caller-supplied sku.
Automatically generates SKU as parentSku-nextFreeIndex and name from parent name + value. Rejects a caller-supplied sku.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `sku` | yes | string | Father article SKU |
| `sku` | yes | string | Parent article SKU |
## Body (JSON)
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `attributeValueId` | no | integer | ID of existing attribute value for the father's attribute |
| `value` | no | string | String value to find or create under the father's attribute |
| `attributeValueId` | no | integer | ID of existing attribute value for the parent's attribute |
| `value` | no | string | String value to find or create under the parent's attribute |
| `name` | no | string | Optional custom variant name |
```json
@@ -48,7 +48,7 @@ Created child article
| Status | Description |
|--------|-------------|
| 400 | Father not configured as father, missing value, duplicate variant, or sku was provided in body |
| 400 | Parent not configured as parent, missing value, duplicate variant, or sku was provided in body |
---

View File

@@ -2,24 +2,24 @@
Protected. Requires a valid session cookie (`credentials: 'include'`).
Attach an existing article as a child to a father article
Attach an existing article as a child to a parent article
Attaches an existing article to this father article. Idempotent if already a child. Returns 409 if the article already belongs to another father.
Attaches an existing article to this parent article. Idempotent if already a child. Returns 409 if the article already belongs to another parent.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `sku` | yes | string | Father article SKU |
| `sku` | yes | string | Parent article SKU |
## Body (JSON)
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `sku` | yes | string | SKU of the existing article to attach as child |
| `attributeValueId` | no | integer | ID of existing attribute value for the father's attribute |
| `value` | no | string | String value to find or create under the father's attribute |
| `reparent` | no | boolean | Whether to allow re-parenting if child already belongs to another father |
| `attributeValueId` | no | integer | ID of existing attribute value for the parent's attribute |
| `value` | no | string | String value to find or create under the parent's attribute |
| `reparent` | no | boolean | Whether to allow re-parenting if child already belongs to another parent |
```json
{
@@ -54,8 +54,8 @@ Attached child article and list of children
| Status | Description |
|--------|-------------|
| 400 | Missing sku or attribute value, or invalid input |
| 404 | Father or child article not found |
| 409 | Article already belongs to another father, or variant value already exists |
| 404 | Parent or child article not found |
| 409 | Article already belongs to another parent, or variant value already exists |
---

View File

@@ -2,22 +2,22 @@
Protected. Requires a valid session cookie (`credentials: 'include'`).
Remove a child article from a father article
Remove a child article from a parent article
Unlinks the child from the father (or deletes if deleteArticle query/body is true).
Unlinks the child from the parent (or deletes if deleteArticle query/body is true).
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `sku` | yes | string | Father article SKU |
| `sku` | yes | string | Parent article SKU |
| `childSku` | yes | string | Child article SKU |
## Query parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `deleteArticle` | no | boolean | If true, permanently deletes the child article (and cascades its entire descendant subtree if it is a father) instead of just unlinking |
| `deleteArticle` | no | boolean | If true, permanently deletes the child article (and cascades its entire descendant subtree if it is a parent) instead of just unlinking |
## Response `200`

View File

@@ -4,7 +4,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
Set options list for an article
Configures options for an article. Some options can be mandated (come with the article in any case). A father article cannot have options. Can be combined with a Stückliste. Pass an empty array to remove options.
Configures options for an article. Some options can be mandated (come with the article in any case). A parent article cannot have options. Can be combined with a Stückliste. Pass an empty array to remove options.
## Path parameters
@@ -53,7 +53,7 @@ Updated options list
| Status | Description |
|--------|-------------|
| 400 | Article is a father, a Stückliste, or contains itself |
| 400 | Article is a parent, a Stückliste, or contains itself |
| 404 | Article or option article not found |
---

View File

@@ -1,17 +1,17 @@
# `POST /article/{sku}/father`
# `POST /article/{sku}/parent`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Toggle father mode for an article
Toggle parent mode for an article
Enables or disables father article status. Enabling requires specifying an
Enables or disables parent article status. Enabling requires specifying an
attributeNameId; disabling requires the article to have zero children.
`attributeNameId` is **conditionally required**: it is optional in the schema
because it is only required when `isFather` is `true`. Omitting it (or passing
`0`/a negative value) together with `isFather: true` returns `400`
`attributeNameId is required to become a father article`. It must not be sent
when `isFather` is `false`.
because it is only required when `isParent` is `true`. Omitting it (or passing
`0`/a negative value) together with `isParent: true` returns `400`
`attributeNameId is required to become a parent article`. It must not be sent
when `isParent` is `false`.
## Path parameters
@@ -24,12 +24,12 @@ when `isFather` is `false`.
| Field | Required | Type | Description |
|-------|----------|------|-------------|
| `isFather` | yes | boolean | True to make article a father, false to revert to regular article |
| `attributeNameId` | no | integer | **Conditionally required when `isFather` is `true`** (required iff isFather=true). The attributename id used for the article's variants. Omitting it while enabling father mode returns 400. Must be omitted when `isFather` is `false`. |
| `isParent` | yes | boolean | True to make article a parent, false to revert to regular article |
| `attributeNameId` | no | integer | **Conditionally required when `isParent` is `true`** (required iff isParent=true). The attributename id used for the article's variants. Omitting it while enabling parent mode returns 400. Must be omitted when `isParent` is `false`. |
```json
{
"isFather": true,
"isParent": true,
"attributeNameId": 1
}
```
@@ -54,9 +54,9 @@ Updated article
| Status | Description |
|--------|-------------|
| 400 | Invalid input, `attributeNameId` missing while `isFather` is true, article still
| 400 | Invalid input, `attributeNameId` missing while `isParent` is true, article still
has children, article has stock/suppliers/a Stückliste/options, or the attribute is
already used by an ancestor father article.
already used by an ancestor parent article.
|
| 404 | Article or attribute name not found |

View File

@@ -112,7 +112,7 @@ Updated article detail
| Status | Description |
|--------|-------------|
| 400 | Invalid prices, POS customer group required, or father article cannot have prices — `{"error":"…"}` |
| 400 | Invalid prices, POS customer group required, or parent article cannot have prices — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Article not found — `{"error":"…"}` |

View File

@@ -4,7 +4,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`).
Articles in category
Returns articles in a category the user is authorized for (omits child articles of a father article). Each article includes `childCount` (the number of its child articles).
Returns articles in a category the user is authorized for (omits child articles of a parent article). Each article includes `childCount` (the number of its child articles).
## Path parameters

View File

@@ -43,8 +43,8 @@ Article detail
"eans": [
"…"
],
"isFather": true,
"fatherSku": "…",
"isParent": true,
"parentSku": "…",
"variants": [
{}
]

View File

@@ -77,7 +77,7 @@ Stock adjusted successfully
| Status | Description |
|--------|-------------|
| 400 | Missing fields, invalid amount, father article cannot have stock, or no company selected — `{"error":"…"}` |
| 400 | Missing fields, invalid amount, parent article cannot have stock, or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Article or warehouse not found — `{"error":"…"}` |

View File

@@ -0,0 +1,58 @@
-- Rename article "father" columns to "parent" (SQL Server / MSSQL).
--
-- One-off manual migration (no migration code is kept in the repo).
-- sqlcmd -S <server> -d <db> -E -i scripts/migrate-parent-rename.mssql.sql
--
-- Wrapped in a transaction and guarded so it is safe to re-run.
-- Drop the existing index explicitly: SQL Server will not let a filtered
-- unique index be carried over to a renamed column.
SET XACT_ABORT ON;
BEGIN TRANSACTION;
-- Drop the old filtered unique index (references fathersort).
IF EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'UQ_article_fatherarticle_fathersort')
DROP INDEX UQ_article_fatherarticle_fathersort ON article;
-- Drop the default constraints bound to the old column names.
IF EXISTS (SELECT 1 FROM sys.default_constraints WHERE name = 'DF_article_is_father')
ALTER TABLE article DROP CONSTRAINT DF_article_is_father;
IF EXISTS (SELECT 1 FROM sys.default_constraints WHERE name = 'DF_article_fathersort')
ALTER TABLE article DROP CONSTRAINT DF_article_fathersort;
-- Drop foreign keys bound to the old columns.
IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_article_fatherarticle')
ALTER TABLE article DROP CONSTRAINT FK_article_fatherarticle;
IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_article_fatherattributename')
ALTER TABLE article DROP CONSTRAINT FK_article_fatherattributename;
-- Rename the columns (sp_rename keeps indexes/FKs tracking the column).
IF COL_LENGTH('article', 'fatherarticle_id') IS NOT NULL
AND COL_LENGTH('article', 'parentarticle_id') IS NULL
EXEC sp_rename 'article.fatherarticle_id', 'parentarticle_id', 'COLUMN';
IF COL_LENGTH('article', 'fatherattributename_id') IS NOT NULL
AND COL_LENGTH('article', 'parentattributename_id') IS NULL
EXEC sp_rename 'article.fatherattributename_id', 'parentattributename_id', 'COLUMN';
IF COL_LENGTH('article', 'fathersort') IS NOT NULL
AND COL_LENGTH('article', 'parentsort') IS NULL
EXEC sp_rename 'article.fathersort', 'parentsort', 'COLUMN';
IF COL_LENGTH('article', 'is_father') IS NOT NULL
AND COL_LENGTH('article', 'is_parent') IS NULL
EXEC sp_rename 'article.is_father', 'is_parent', 'COLUMN';
-- Recreate the constraints under their new names.
IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_article_parentarticle')
ALTER TABLE article ADD CONSTRAINT FK_article_parentarticle
FOREIGN KEY (parentarticle_id) REFERENCES article(id) ON DELETE NO ACTION;
IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_article_parentattributename')
ALTER TABLE article ADD CONSTRAINT FK_article_parentattributename
FOREIGN KEY (parentattributename_id) REFERENCES attributename(id) ON DELETE NO ACTION;
ALTER TABLE article ADD CONSTRAINT DF_article_is_parent DEFAULT (0) FOR is_parent;
ALTER TABLE article ADD CONSTRAINT DF_article_parentsort DEFAULT (0) FOR parentsort;
CREATE UNIQUE INDEX UQ_article_parentarticle_parentsort
ON article (parentarticle_id, parentsort)
WHERE parentarticle_id IS NOT NULL;
COMMIT TRANSACTION;

View File

@@ -0,0 +1,14 @@
-- Rename article "father" columns to "parent".
--
-- One-off manual migration (no migration code is kept in the repo).
-- sqlite3 data/dash.db < scripts/migrate-parent-rename.sqlite.sql
--
-- Safe to re-run: every step is guarded by a column-existence check.
ALTER TABLE article RENAME COLUMN fatherarticle_id TO parentarticle_id;
ALTER TABLE article RENAME COLUMN fatherattributename_id TO parentattributename_id;
ALTER TABLE article RENAME COLUMN fathersort TO parentsort;
ALTER TABLE article RENAME COLUMN is_father TO is_parent;
-- Verify
PRAGMA table_info(article);