belongsToMany relation CRUD examples using OpWithRelation functions
In Supista ERP, a belongsToMany relation is used to define a many-to-many relationship between two components, where:
One record in Table A can be linked to many records in Table B
And one record in Table B can also be linked to many records in Table A
This is made possible using an automatically generated junction table (also called a relation or pivot table), which holds the IDs of both linked records.
Example Scenario: Members ↔ Projects
A Member can work on multiple Projects
A Project can have multiple Members
In this case:
Members and Projects are the two main tables
Supista ERP auto-generates a relation table (e.g., Projects - Members Relation) with:
Member ID -> __d3__id
Project ID -> __d3__parentId
createOpWithRelation
Adds a new relation row in the junction table, linking Member ↔ Project
Example Code:
async function customizeERP(userData, apiOperations) {
const parentTableName = "members";
return new Promise(async (resolve, reject) => {
const payload = {
__d3__newData: { ...userData?.__d3__newData }
};
const createdData = await createOpWithRelation(
parentTableName,
payload,
userData?.__d3__parentId,
userData?.__d3__relationId
);
if (createdData[0]) {
resolve({ result: createdData[0] });
} else {
resolve({
result: {},
popupMsg: {
type: "warning",
message: "Could not read data! Please try again later"
}
});
}
});
}Example input:
{
"__d3__newData": {
"__d3__id": [
2
]
},
"__d3__parentId": 2,
"__d3__relationId": "732f6s3717"
}Example Output:
{
"response": {
"__d3__result": {
"result": {
"projects Id": 2,
"members Id": 2,
"__d3__createdAt": "2025-06-18T11:53:37.614Z",
"__d3__updatedAt": "2025-06-18T11:53:37.614Z",
"__d3__deletedAt": null
}
},
"__d3__error": false,
"printConsole": []
}
}readOpWithRelation
Fetches all related records for a parent entity (e.g., all Members in a Project)
Example Code:
async function customizeERP(userData, apiOperations) {
const parentTableName = "members";
return new Promise(async (resolve, reject) => {
const payload = {
__d3__filterdata: { where: {} }
};
const rowsData = await readOpWithRelation(
parentTableName,
payload,
userData?.__d3__parentId,
userData?.__d3__relationId
);
if (rowsData?.count) {
resolve({ result: rowsData });
} else {
resolve({
result: {},
popupMsg: {
type: "error",
message: "Could Not read data! Please try again later"
}
});
}
});
}Example input:
{
"__d3__filterdata": {
"where": {}
},
"__d3__parentId": 1,
"__d3__relationId": "732f6s3717"
}Example Output:
{
"response": {
"__d3__result": {
"result": {
"count": 2,
"rows": [
{
"id": 2,
"project title": "AI/ML",
"__d3__createdBy": 575,
"__d3__lastUpdatedBy": null,
"__d3__createdAt": "2025-06-17T08:59:23.289Z",
"__d3__updatedAt": "2025-06-17T08:59:23.289Z",
"__d3__deletedAt": null
},
{
"id": 1,
"project title": "Web3",
"__d3__createdBy": 575,
"__d3__lastUpdatedBy": null,
"__d3__createdAt": "2025-06-16T20:24:46.032Z",
"__d3__updatedAt": "2025-06-16T20:24:46.032Z",
"__d3__deletedAt": null
}
]
}
},
"__d3__error": false,
"printConsole": []
}
}updateOpWithRelation
Updates metadata or related values in the junction row (if any exist)
Example Code:
async function customizeERP(userData, apiOperations) {
const parentTableName = "members";
const { updateOp } = apiOperations;
return new Promise(async (resolve, reject) => {
const payload = {
__d3__id: userData?.__d3__id,
__d3__newData: { ...userData?.__d3__newData }
};
const updatedData = await updateOpWithRelation(
parentTableName,
payload,
userData?.__d3__parentId,
userData?.__d3__relationId
);
if (updatedData?.affectedCount?.[0]) {
resolve({ result: updatedData });
} else {
resolve({
result: {},
popupMsg: {
type: "error",
message: "Could Not update data! Please try again later"
}
});
}
});
}Example input:
{
"__d3__newData": {
"id": 2,
"project title": "AI/ML/design",
"__d3__createdBy": 575,
"__d3__lastUpdatedBy": null,
"__d3__createdAt": "2025-06-17T08:59:23.289Z",
"__d3__updatedAt": "2025-06-17T08:59:23.289Z",
"__d3__deletedAt": null
},
"__d3__id": 2,
"__d3__parentId": 1,
"__d3__relationId": "732f6s3717"
}Example Output:
{
"response": {
"__d3__result": {
"result": {
"affectedCount": [
1
]
}
},
"__d3__error": false,
"printConsole": []
}
}removeOpWithRelation
Removes the link between a Member and a Project
Example Code:
async function customizeERP(userData, apiOperations) {
const parentTableName = "members";
return new Promise(async (resolve, reject) => {
const payload = {
__d3__id: [userData?.__d3__id]
};
const deletedData = await removeOpWithRelation(
parentTableName,
payload,
userData?.__d3__parentId,
userData?.__d3__relationId
);
if (deletedData?.deletedObjNo) {
resolve({ result: deletedData });
} else {
resolve({
result: {},
popupMsg: {
type: "error",
message: "Could Not delete data! Please try again later"
}
});
}
});
}Example input:
{
"__d3__parentId": 1,
"__d3__id": 2,
"__d3__relationId": "732f6s3717"
}Example Output:
{
"response": {
"__d3__result": {
"result": {
"deletedObjNo": 1
}
},
"__d3__error": false,
"printConsole": []
}
}