Skip to content

Commit 9d4d3bb

Browse files
Merge pull request #123 from VineetBala-AOT/main
Changes to Amendment conditions - numbering rules
2 parents e21aade + 407bbed commit 9d4d3bb

File tree

25 files changed

+503
-133
lines changed

25 files changed

+503
-133
lines changed

.github/workflows/web.ci.yml

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
name: CONDITION WEB CI
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
paths:
8+
- "condition-web/**"
9+
push:
10+
branches:
11+
- main
12+
workflow_dispatch:
13+
inputs:
14+
environment:
15+
description: "Environment (dev/test/prod)"
16+
required: true
17+
default: "dev"
18+
19+
defaults:
20+
run:
21+
shell: bash
22+
working-directory: ./condition-web
23+
24+
jobs:
25+
setup-job:
26+
runs-on: ubuntu-22.04
27+
28+
if: github.repository == 'bcgov/EPIC.conditions'
29+
30+
steps:
31+
- uses: actions/checkout@v2
32+
- run: "true"
33+
34+
linting:
35+
needs: setup-job
36+
runs-on: ubuntu-22.04
37+
38+
strategy:
39+
matrix:
40+
node-version: [18.x]
41+
42+
steps:
43+
- uses: actions/checkout@v2
44+
- name: Use Node.js ${{ matrix.node-version }}
45+
uses: actions/setup-node@v1
46+
with:
47+
node-version: ${{ matrix.node-version }}
48+
- name: Install dependencies
49+
run: |
50+
npm install --legacy-peer-deps
51+
- name: Lint
52+
id: lint
53+
run: |
54+
npm run lint
55+
56+
testing:
57+
needs: setup-job
58+
runs-on: ubuntu-22.04
59+
60+
steps:
61+
- uses: actions/checkout@v2
62+
63+
- name: Use Node.js ${{ matrix.node-version }}
64+
uses: actions/setup-node@v1
65+
with:
66+
node-version: ${{ matrix.node-version }}
67+
68+
- name: Install dependencies
69+
run: |
70+
npm install --legacy-peer-deps
71+
72+
- name: Test with Cypress
73+
id: test
74+
run: |
75+
npx cypress run --component --headed --browser chrome
76+
77+
# - name: Sets Codecov branch name
78+
# run: |
79+
# echo "CODECOV_BRANCH=PR_${{ github.head_ref }}" >> $GITHUB_ENV
80+
# if: github.event_name == 'pull_request'
81+
82+
# - name: Upload coverage to Codecov
83+
# uses: codecov/codecov-action@v4
84+
# with:
85+
# flags: condition-web
86+
# name: codecov-condition-web
87+
# fail_ci_if_error: true
88+
# verbose: true
89+
# override_branch: ${{ env.CODECOV_BRANCH }}
90+
# token: ${{ secrets.CODECOV_TOKEN }}
91+
# directory: ./condition-web/coverage
92+
93+
- name: Coverage Report
94+
run: |
95+
npx nyc report --reporter=lcov --reporter=text-summary
96+
97+
build-check:
98+
needs: setup-job
99+
runs-on: ubuntu-22.04
100+
101+
steps:
102+
- uses: actions/checkout@v2
103+
- name: Use Node.js ${{ matrix.node-version }}
104+
uses: actions/setup-node@v1
105+
with:
106+
node-version: ${{ matrix.node-version }}
107+
- name: Install dependencies
108+
run: |
109+
npm install --legacy-peer-deps
110+
- name: build to check strictness
111+
id: build
112+
run: |
113+
npm run build --quiet || true
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""remove_cascade_from_project
2+
3+
Revision ID: d909569f8c4e
4+
Revises: af25a1d94134
5+
Create Date: 2025-08-11 14:39:30.881474
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = 'd909569f8c4e'
14+
down_revision = 'af25a1d94134'
15+
branch_labels = None
16+
depends_on = None
17+
18+
OLD_FK_NAME = 'documents_project_id_fkey'
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
# Drop the existing FK constraint
23+
op.drop_constraint(OLD_FK_NAME, 'documents', schema='condition', type_='foreignkey')
24+
25+
# Recreate FK with RESTRICT (or just omit ondelete to default to restrict)
26+
op.create_foreign_key(
27+
OLD_FK_NAME,
28+
'documents', 'projects',
29+
['project_id'], ['project_id'],
30+
source_schema='condition',
31+
referent_schema='condition',
32+
ondelete='RESTRICT'
33+
)
34+
# ### end Alembic commands ###
35+
36+
37+
def downgrade():
38+
# ### commands auto generated by Alembic - please adjust! ###
39+
op.drop_constraint(OLD_FK_NAME, 'documents', schema='condition', type_='foreignkey')
40+
41+
op.create_foreign_key(
42+
OLD_FK_NAME,
43+
'documents', 'projects',
44+
['project_id'], ['project_id'],
45+
source_schema='condition',
46+
referent_schema='condition',
47+
ondelete='CASCADE'
48+
)
49+
# ### end Alembic commands ###

condition-api/src/condition_api/models/document.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Document(BaseModel):
3131
is_latest_amendment_added = Column(Boolean)
3232

3333
# Foreign key to link to the project
34-
project_id = Column(String(255), ForeignKey('condition.projects.project_id', ondelete='CASCADE'))
34+
project_id = Column(String(255), ForeignKey('condition.projects.project_id', ondelete='RESTRICT'))
3535

3636
# Establish the relationship back to the Project table
3737
project = relationship('Project', back_populates='documents')

condition-api/src/condition_api/models/project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class Project(BaseModel):
2323
project_type = Column(Text)
2424

2525
# Establish a one-to-many relationship with the Document table
26-
documents = relationship('Document', back_populates='project', cascade='all, delete-orphan')
26+
documents = relationship('Document', back_populates='project')
2727

2828
__table_args__ = (
2929
UniqueConstraint('project_id', name='uq_project'),

condition-api/src/condition_api/resources/condition.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,18 @@ def post(project_id, document_id):
149149
conditions_data = ConditionSchema().load(API.payload)
150150
else:
151151
conditions_data = {}
152-
created_condition = ConditionService.create_condition(project_id, document_id, conditions_data)
152+
query_params = request.args
153+
allow_duplicate_condition = query_params.get(
154+
'allow_duplicate_condition', 'true').lower() == 'true'
155+
created_condition = ConditionService.create_condition(project_id,
156+
document_id,
157+
conditions_data,
158+
allow_duplicate_condition)
153159
return created_condition, HTTPStatus.OK
154160
except ValidationError as err:
155161
return {"message": str(err)}, HTTPStatus.BAD_REQUEST
162+
except ConditionNumberExistsError as err:
163+
return {"message": str(err)}, HTTPStatus.CONFLICT
156164

157165

158166
@cors_preflight("GET, OPTIONS, PATCH, DELETE")
@@ -194,11 +202,22 @@ def patch(condition_id):
194202
try:
195203
conditions_data = ConditionSchema().load(API.payload)
196204
query_params = request.args
197-
check_condition_over_project = query_params.get(
198-
'check_condition_over_project', 'true').lower() == 'true'
205+
206+
allow_duplicate_condition = query_params.get(
207+
'allow_duplicate_condition', 'true').lower() == 'true'
208+
if allow_duplicate_condition:
209+
check_condition_exists = True
210+
check_condition_over_project = False
211+
else:
212+
check_condition_exists = True
213+
check_condition_over_project = query_params.get(
214+
'check_condition_over_project', 'true'
215+
).lower() == 'true'
216+
199217
updated_condition = ConditionService.update_condition(conditions_data,
200-
condition_id, True,
201-
check_condition_over_project)
218+
condition_id, check_condition_exists,
219+
check_condition_over_project,
220+
allow_duplicate_condition)
202221
return ConditionSchema().dump(updated_condition), HTTPStatus.OK
203222
except ValidationError as err:
204223
return {"message": str(err)}, HTTPStatus.BAD_REQUEST

condition-api/src/condition_api/services/condition_service.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ def update_condition(
321321
condition_id=None,
322322
check_condition_exists=None,
323323
check_condition_over_project=None,
324+
allow_duplicate_condition=None,
324325
):
325326
"""
326327
Update the approved status, topic tags, and subconditions of a specific condition.
@@ -341,7 +342,7 @@ def update_condition(
341342
if check_condition_over_project:
342343
ConditionService._check_condition_conflict_in_project(condition, condition_number)
343344
# Mark existing condition inactive if needed
344-
if condition.amended_document_id and not check_condition_over_project:
345+
if condition.amended_document_id and not check_condition_over_project and not allow_duplicate_condition:
345346
ConditionService._deactivate_existing_condition(condition, condition_number)
346347

347348
# Update condition fields
@@ -497,7 +498,7 @@ def upsert_subconditions(condition_id, subconditions, parent_id=None):
497498
condition_id, subcond_data["subconditions"], parent_id=subcondition_id)
498499

499500
@staticmethod
500-
def create_condition(project_id, document_id, conditions_data):
501+
def create_condition(project_id, document_id, conditions_data, allow_duplicate_condition=None):
501502
"""Create a new condition."""
502503
amendment = (
503504
db.session.query(Amendment.document_id)
@@ -524,8 +525,18 @@ def create_condition(project_id, document_id, conditions_data):
524525
.first()
525526
)
526527

528+
if allow_duplicate_condition:
529+
ConditionService._check_duplicate_condition_number(
530+
condition=Condition(
531+
document_id=final_document_id,
532+
amended_document_id=amended_document_id
533+
),
534+
condition_id=None, # None since we are creating a new one
535+
condition_number=conditions_data.get("condition_number")
536+
)
537+
527538
# If it exists, update is_active to False
528-
if existing_condition:
539+
if existing_condition and not allow_duplicate_condition:
529540
existing_condition.is_active = False
530541
existing_condition.effective_to = datetime.utcnow()
531542
db.session.add(existing_condition)

condition-lib/loadConditions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ def insert_subconditions(condition_id, parent_subcondition_id, subconditions):
4545

4646
def get_document_category_id(document_type):
4747
"""Determine the document_category_id based on document_type."""
48-
if document_type == "Order":
48+
if document_type == "Exemption Order":
4949
return 2
50-
elif document_type == "Other":
50+
elif document_type == "Other Order":
5151
return 4
5252
elif document_type == "Amendment":
5353
return 3

condition-web/src/components/ConditionDetails/ConditionAttribute/Independent/ConditionAttributeRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const ConditionAttributeRow: React.FC<ConditionAttributeRowProps> = ({
6969

7070
useEffect(() => {
7171
onEditModeChange?.(isEditable);
72-
}, [isEditable]);
72+
}, [isEditable, onEditModeChange]);
7373

7474
const [chips, setChips] = useState<string[]>(
7575
conditionKey === CONDITION_KEYS.PARTIES_REQUIRED

condition-web/src/components/ConditionDetails/ConditionAttribute/Independent/ConditionAttributeTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ const ConditionAttributeTable = memo(({
126126
setIsIEMRequired(!!IEMRequired);
127127
}
128128

129-
}, [conditionAttributeDetails?.independent_attributes]);
129+
}, [conditionAttributeDetails?.independent_attributes, setCondition, setIsConsultationRequired, setIsIEMRequired]);
130130

131131
const approveConditionAttributes = () => {
132132
if (isAnyRowEditing) {

condition-web/src/components/ConditionDetails/ConditionAttribute/ManagementPlan/ManagementPlanAccordion.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ const ManagementPlanAccordion: React.FC<Props> = ({
165165

166166
afterSuccess?.();
167167
} catch (err) {
168-
console.error("Error updating management plan:", err);
168+
notify.error("Failed to update management plan.");
169169
}
170170
};
171171

@@ -213,7 +213,7 @@ const ManagementPlanAccordion: React.FC<Props> = ({
213213
);
214214
setIsIEMRequired(!!IEMRequired);
215215
}
216-
}, [conditionAttributeDetails]);
216+
}, [conditionAttributeDetails, setCondition]);
217217

218218
const handleApprovePlan = async (e: React.MouseEvent) => {
219219
e.stopPropagation();

0 commit comments

Comments
 (0)