Skip to content

Commit

Permalink
feat: update records (#673)
Browse files Browse the repository at this point in the history
  • Loading branch information
marianfoo authored Feb 7, 2025
1 parent 5d67e69 commit 6fe3c90
Show file tree
Hide file tree
Showing 58 changed files with 2,323 additions and 193 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/opa5-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
jobs:
test-opa5:
runs-on: ubuntu-latest
if: "github.event.pull_request.title != 'chore: release main'"
if: "github.event.pull_request.title != 'chore: release main' && !github.event.pull_request.draft"
strategy:
fail-fast: false
matrix:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ui5-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
jobs:
test-ui5-linter:
runs-on: ubuntu-latest
if: "github.event.pull_request.title != 'chore: release main'"
if: "github.event.pull_request.title != 'chore: release main' && !github.event.pull_request.draft"

steps:

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/wdi5-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
jobs:
test-wdi5:
runs-on: ubuntu-latest
if: "github.event.pull_request.title != 'chore: release main'"
if: "github.event.pull_request.title != 'chore: release main' && !github.event.pull_request.draft"
strategy:
fail-fast: false
matrix:
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"packages/ui5-cc-spreadsheetimporter": "1.5.2"
"packages/ui5-cc-spreadsheetimporter": "1.6.0"
}
6 changes: 4 additions & 2 deletions dev/testapps.json
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,8 @@
"testMapping": {
"specs": [
"../test/specs/all/**.test.js",
"../test/specs/v4/**.test.js"
"../test/specs/v4/**.test.js",
"../test/specs/update/**.test.js"
]
},
"copyVersions": [
Expand Down Expand Up @@ -320,7 +321,8 @@
"appTitel": "Orders V4 FS 120",
"testMapping": {
"specs": [
"../test/specs/download/**.test.js"
"../test/specs/download/**.test.js",
"../test/specs/updatefreestyle/**.test.js"
]
},
"copyVersions": []
Expand Down
17 changes: 16 additions & 1 deletion docs/pages/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

How to use them see [Example Code](#example-code)

## Configuration Options
## Configuration Overview

The table below summarizes the options available for the UI5 Spreadsheet Importer Component. Detailed explanations and examples for each option are provided in the linked sections.

Expand Down Expand Up @@ -31,6 +31,7 @@ The table below summarizes the options available for the UI5 Spreadsheet Importe

| Option | Description | Default | Type |
|-----------------------------------------------------|-------------------------------------------------------|-----------------|------------|
| [`action`](#action) | Continues processing next batches even after errors. | `CREATE` | `string` |
| [`batchSize`](#batchsize) | Controls the size of batches sent to the backend. | `1000` | `number` |
| [`strict`](#strict) | Controls availability of the "Continue" button in error dialogs. | `false` | `boolean` |
| [`decimalSeparator`](#decimalseparator) | Sets the decimal separator for numbers. | Browser default | `string` |
Expand All @@ -39,6 +40,7 @@ The table below summarizes the options available for the UI5 Spreadsheet Importe
| [`skipColumnsCheck`](#skipcolumnscheck) | Skips the check for unknown columns not in metadata. | `false` | `boolean` |
| [`continueOnError`](#continueonerror) | Continues processing next batches even after errors. | `false` | `boolean` |


### Advanced Configuration Options

| Option | Description | Default | Type |
Expand All @@ -57,6 +59,8 @@ The table below summarizes the options available for the UI5 Spreadsheet Importe

---

## Configuration Options

### `columns`

**default:** all fields
Expand Down Expand Up @@ -176,6 +180,15 @@ Of course, creating the draft entity and the subsequent activation takes longer
Together with the option `continueOnError`, it is also possible to create all entities and try to activate the other entities if the draft activation fails.
This means that at least all drafts are available.

### `action`

**default:** `CREATE`

Options:

- `CREATE` : Create
- `UPDATE` : Update

### `batchSize`

**default:** `1.000`
Expand All @@ -192,6 +205,8 @@ The default value is 1,000, which means that when the number of lines in the Spr

If you set the `batchSize` to 0, the payload array will not be divided, and the entire array will be sent as a single batch request.

For updates, the batch size is limited to 100.

### `standalone`

**default:** `false`
Expand Down
54 changes: 54 additions & 0 deletions docs/pages/Development/Update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@

Only V4 is supported for now.

## OData V4

The problem with Draft is that when updating lot of objects, the update will fail if one of the objects is not found because of the draft status.
Draft status will determined with `IsActiveEntity` property.
To make it as seamless as possible, the process will try to find every object with `IsActiveEntity=true`. This does not find objects that dont have a active entity (draft but not yet created).
The finding of the object result in a get request to the OData service for each row.
After that the process knows the state of the object and can update it.
So if on the object `HasDraftEntity` is true or `IsActiveEntity` is false, the process will create a new context with `IsActiveEntity=false` and use the draft entity automatically to update the object.


## Technical Details

To get the all the objects that are imported from the spreadsheet, the process will create a new empty list binding with a filter of all the keys from the spreadsheet.
Technically is has to query for `IsActiveEntity=true` and `IsActiveEntity=false` and combine the results.
This will result in two get requests to the OData service for each row combined in two batch request for each batch.
If a row is not found it is just not included in the List Binding.
So the process will not fail if a row is not found and can match which objects are not found from the List Binding.
If a object was not found the user can then decide to continue with the found objects or to cancel the process.
Each context will then be used to update the object with `setProperty`.

### Different States in Export

For the export the process determines the state of the object by checking the `IsActiveEntity` and `HasDraftEntity` properties.


#### List Report

- `IsActiveEntity=true` and `HasDraftEntity=false` -> `IsActiveEntity` column is set to true
- `IsActiveEntity=true` and `HasDraftEntity=true` -> `IsActiveEntity` column is set to false

#### Object Page

- `IsActiveEntity=true` and `HasDraftEntity=false` -> `IsActiveEntity` column is set to true
- `IsActiveEntity=true` and `HasDraftEntity=true` -> `IsActiveEntity` column is set to false

### Different States in Upload

#### List Report

- `IsActiveEntity=true` and `HasDraftEntity=false` -> update the object (expecting `IsActiveEntity=true` in the spreadsheet import)
- `IsActiveEntity=true` and `HasDraftEntity=true` -> create a new context with `IsActiveEntity=false` and use the draft entity automatically to update the object (expecting `IsActiveEntity=false` in the spreadsheet import)

#### Table in Object Page

##### Not in Edit Mode

- `IsActiveEntity=true` and `HasDraftEntity=false` and `HasActiveEntity=false` -> update the object (expecting `IsActiveEntity=true` in the spreadsheet import)

##### In Edit Mode

- `IsActiveEntity=false` and `HasDraftEntity=false` and `HasActiveEntity=true` -> update the object (expecting `IsActiveEntity=false` in the spreadsheet import)
95 changes: 95 additions & 0 deletions docs/pages/Update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
!!! warning
This feature is currently experimental and may not work as expected.
Also only available for OData V4.
Please provide feedback: https://github.com/spreadsheetimporter/ui5-cc-spreadsheetimporter/issues


## Usage

It is recommended, especially for Entities with GUID, to first download the data with the Spreadsheet Importer and include the keys.

1. Download the data with the Spreadsheet Importer and include the keys.
2. Edit the spreadsheet
3. Upload the data with the Spreadsheet Importer.

## Getting started

The minimal configuration to update entities instead of creating is:

```js
this.spreadsheetUploadUpdate = await this.editFlow.getView().getController().getAppComponent()
.createComponent({
usage: "spreadsheetImporter",
async: true,
componentData: {
context: this,
tableId: "ui.v4.ordersv4fe::OrdersObjectPage--fe::table::Items::LineItem-innerTable",
action: "UPDATE",
deepDownloadConfig: {
addKeysToExport: true,
showOptions: false,
filename: "Items"
},
showDownloadButton: true
}
});
```

This configuration will show the download button and download all the available data for the referenced table.
When you press the download button, the data will be downloaded including the keys necessary for the update.

You can then change the data in the spreadsheet and upload the data again.
By default, only the changed properties are updated (partial update). You can change this by setting the `fullUpdate` property to `true` (see [Configuration](#configuration) below).

## How it works

When you upload the file to the App, it will do the usual checks if the columns are in the data model and the data is valid (see [Checks](./Checks.md)).
When the user presses the upload button, it will fetch all the data in the batch. To make sure all the data is fetched, it will fetch the data for active and draft separately. So for every batch a ListBinding is created and two requests are made with filters for the keys and for `IsActiveEntity = true` and `IsActiveEntity = false`. This is needed because of the separation of active and draft (see [why requests fail in CAP with OData draft enabled](https://cap.cloud.sap/docs/get-started/troubleshooting#why-do-some-requests-fail-if-i-set-odata-draft-enabled-on-my-entity)).

This data is used to determine if the object is in draft or active mode, if the object exists at all, and for partial updates whether a field is changed.

For every change, an `ODataContextBinding` is created and the data is updated.

## Things to consider / Drawbacks

### IsActiveEntity handling

The column `IsActiveEntity` states the current state of the object (Draft or Active). In the spreadsheet file, the current state must match the state of the object.

• If the state is wrong, a warning is shown and the user can still continue. If the user continues, the object will be updated in the current state that the object is actually in.
For example, if in the spreadsheet file the `IsActiveEntity` column is set to `true` but the object is in draft mode, a warning will be shown, and if the user continues, the draft object will be updated with the data from the spreadsheet.

### Download only Active Entities

If you download the data with the Spreadsheet Importer, only the active entities are downloaded. If you then update the data, the object will be updated in the current state that the object is in.
So if the object is in draft mode, the data from the active state will still be used to update the draft object.

### Performance and batch size

Because the update needs extra requests (fetch of active and draft objects, plus partial updates), the update is slower than a create operation. For mass updates, this can take some time.
Because of the performance considerations, the batch size for update is limited to 100 per batch.

### Filter Limitations

When exporting the data, currently all the data is exported. Any filters in a List Report are not respected at the moment.

## Configuration

Below is a brief overview of the main configuration options relevant to updating. For the complete list, see the [Configuration documentation](./Configuration.md).

| Option | Description | Default |
| --------------- | ------------------------------------------------------------------------------ | ------- |
| `fullUpdate` | Update all properties of the object (true). If false, only changed properties are updated. | false |
| `columns` | Columns to update. | all |

### fullUpdate

If `fullUpdate` is set to `true`, the component updates all properties of the object.
If `fullUpdate` is set to `false` (default), only the properties that have changed are updated (partial update).

### columns

The `columns` property is an array of strings. The strings are the names of the columns that should be updated.
Columns that are not in the array will not be updated at all. This is useful if you only want to update a subset of properties.

When using `fullUpdate = true`, the system will still honor `columns`—only those columns listed will be sent in the update request. For unlisted columns, no updates will be sent.
2 changes: 1 addition & 1 deletion examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"wdio-chromedriver-service": "8.1.1",
"wdio-timeline-reporter": "5.1.4",
"wdio-ui5-service": "3.0.0-rc.0",
"webdriverio": "^9.4.1",
"webdriverio": "^9.5.1",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ sap.ui.define(["sap/m/MessageToast"], function (MessageToast) {
async: true,
componentData: {
context: this,
// action: "UPDATE",
// updateConfig: {
// fullUpdate: false,
// columns: ["OrderNo"]
// },
createActiveEntity: true,
i18nModel: this.getModel("i18n"),
debug: true,
Expand Down
72 changes: 72 additions & 0 deletions examples/packages/ordersv4fe/webapp/ext/ObjectPageExtController.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ sap.ui.define([], function () {
async: true,
componentData: {
context: this,

useTableSelector: true,
i18nModel: this.getModel("i18n")
}
Expand Down Expand Up @@ -77,11 +78,19 @@ sap.ui.define([], function () {
async: true,
componentData: {
context: this,
// action: "UPDATE",
// updateConfig: {
// fullUpdate: false
// },
showDownloadButton: true,
tableId: "ui.v4.ordersv4fe::OrdersObjectPage--fe::table::Items::LineItem-innerTable",
columns: ["product_ID", "quantity", "title", "price", "validFrom", "timestamp", "date", "time", "boolean", "decimal", "byte","binary"],
mandatoryFields: ["product_ID", "quantity"],
spreadsheetFileName: "Test.xlsx",
i18nModel: this.getModel("i18n"),
deepDownloadConfig: {
depth: 0
},
sampleData: [
{
product_ID: "HT-1000",
Expand Down Expand Up @@ -180,6 +189,69 @@ sap.ui.define([], function () {
});
this.spreadsheetUploadTableShippingInfo.openSpreadsheetUploadDialog();
this.editFlow.getView().setBusy(false);
},

openSpreadsheetUploadDialogTableUpdate: async function (oEvent) {
this.spreadsheetUploadUpdate = await this.editFlow.getView().getController().getAppComponent()
.createComponent({
usage: "spreadsheetImporter",
async: true,
componentData: {
context: this,
tableId: "ui.v4.ordersv4fe::OrdersObjectPage--fe::table::Items::LineItem-innerTable",
action: "UPDATE",
updateConfig: {
fullUpdate: false
},
deepDownloadConfig: {
addKeysToExport: true,
showOptions: false,
filename: "OrderItems"
},
showDownloadButton: true,
tableId: "ui.v4.ordersv4fe::OrdersObjectPage--fe::table::Items::LineItem-innerTable",
columns: ["product_ID", "quantity", "title", "price", "validFrom", "timestamp", "date", "time", "boolean", "decimal", "byte","binary"],
mandatoryFields: ["product_ID", "quantity"]
}
});
this.spreadsheetUploadUpdate.openSpreadsheetUploadDialog();
},

openSpreadsheetUpdateDialog: async function (oEvent) {
this.spreadsheetUpload = await this.editFlow.getView().getController().getAppComponent()
.createComponent({
usage: "spreadsheetImporter",
async: true,
componentData: {
context: this,
tableId: "ui.v4.ordersv4fe::OrdersObjectPage--fe::table::Items::LineItem-innerTable",
action: "UPDATE",
updateConfig: {
fullUpdate: false
},
}
});
this.spreadsheetUpload.openSpreadsheetUploadDialog();
},

dowloadItems: async function(event) {
this.spreadsheetUpload = await this.editFlow.getView().getController().getAppComponent()
.createComponent({
usage: "spreadsheetImporter",
async: true,
componentData: {
context: this,
tableId: "ui.v4.ordersv4fe::OrdersObjectPage--fe::table::Items::LineItem-innerTable",
createActiveEntity: true,
debug: false,
deepDownloadConfig: {
addKeysToExport: true,
showOptions: false,
filename: "OrderItems"
}
}
});
this.spreadsheetUpload.triggerDownloadSpreadsheet();
}
};
});
Loading

0 comments on commit 6fe3c90

Please sign in to comment.