"""Per-category live QA semantics, resource permissions and internal workflow. No visual claim.""" import importlib.util import json import uuid import copy from datetime import datetime, timezone from pathlib import Path spec=importlib.util.spec_from_file_location('opportunity_workflow_http_qa',Path(__file__).with_name('opportunity-workflow-http-qa.py')) m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m) m.path=m.PRIVATE/'opportunity-semantic-http-qa.json' proof=m.proof authority=json.loads((m.PRIVATE/'opportunity-semantic-fixtures-qa.json').read_text())['authority_id'] def enrich(p,doc): p['currency']='MZN';p['review_due_date']='2027-01-01' if p['opportunity_category']=='investment_project': p['markets'][0]['country_code']='FR' for i,row in enumerate(p['milestones']): row.update({'start_date':row['due_date'],'end_date':row['due_date'],'responsible_name':'Responsável declarado sintético QA','dependencies':[] if i==0 else [p['milestones'][0]['title']]}) if 'real_estate' in p: p['real_estate'].update({'title_document_id':doc,'title_holder_name':'Titular sintético da organização QA','issuing_authority_id':authority, 'utilities':[{'type':t,'status':'existing'} for t in ['electricity','water','sanitation','telecommunications','access']]}) if 'land_rights' in p: p['land_rights'].update({'holder_document_id':doc,'communities_affected':False,'exploitation_plan_required':False}) if 'business_transaction' in p: bt=p['business_transaction'];bt.update({'available_financial_years':3,'entity_registration':{'tax_identifier':'QA-SYNTHETIC','registration_number':'QA-ONLY','legal_document_id':doc,'beneficial_owner_document_id':doc}, 'financial_periods':[{'year':y,'currency':'MZN','revenue':500,'profit':50,'assets':300,'liabilities':100,'cash_flow':50,'document_id':doc} for y in [2023,2024,2025]], 'assets_liabilities':{'assets':'Ativos sintéticos QA','excluded_assets':'Nenhum no ensaio','liabilities':'Passivos sintéticos QA','guarantees':'Nenhuma no ensaio','reconciliation_document_id':doc}, 'employees':{'total':2,'contracts':'Vínculos sintéticos','obligations':'Obrigações sintéticas','labor_liabilities':'Nenhum passivo no cenário sintético'}, 'contracts_licenses':[{'name':'Licença fictícia QA','transferability':'transferable','expiry_date':'2027-12-31','document_id':doc}], 'litigation_contingencies':[{'type':'none'}]}) bt['valuation'].update({'appraiser':'Avaliador sintético QA','document_id':doc}) if 'governance_rights' in bt:bt['governance_rights'].update({'board':'Conselho sintético','reserved_matters':'Matérias reservadas sintéticas','dividends':'Dividendos sintéticos','exit':'Saída sintética','deadlock':'Mecanismo sintético de impasse'}) if 'partner_contribution' in bt:bt['partner_contribution'].update({'assets':'Ativos sintéticos','technology':'Tecnologia sintética','market':'Mercado sintético','management':'Gestão sintética'}) if 'franchise_terms' in bt:bt['franchise_terms'].update({'initial_fee':1000,'royalties':'Encargos sintéticos descritos','support':'Suporte sintético','exclusivity':'Sem exclusividade no cenário','renewal':'Renovação sintética','termination':'Rescisão sintética','brand_license_document_id':doc}) return p def negative(id,p,path,change): bad=copy.deepcopy(p);change(bad);m.save(m.promoter,id,bad) response=m.transition(m.promoter,id,'submit',status=422) assert path in response['errors'],(path,list(response.get('errors',{}))) recovered=m.page(m.Session('promoter').login(),id) assert recovered['values']['canonical']==m.normalized(bad) proof.setdefault('negative_fields',[]).append({'id':id,'field':path,'status':422,'incomplete_draft_recovered':True}) try: # Normal administrator assignment; synthetic reviewer, no real institutional responsibility inferred. assignments=m.superuser.call('GET','/perfis/gestao',inertia=True)['props']['assignments'] if not any(g['user_id']==m.actors['bootstrap']['id'] and g['role']=='validator' and g['revoked_at'] is None and 'technical_reviewer' in json.loads(g['responsibilities']) and set(m.types).issubset(json.loads(g['opportunity_types'])) for g in assignments): m.superuser.call('POST','/perfis/atribuicoes',{'user_id':m.actors['bootstrap']['id'],'role':'validator','opportunity_types':m.types, 'responsibilities':['technical_reviewer','legal_reviewer','appeal_reviewer'],'reason':'Responsabilidades sintéticas QA para pareceres; nenhuma competência real atribuída'},status=201) reviewer=m.Session('bootstrap').login() for category in m.types: id,p,doc=m.create(category) case=proof['cases'][-1] if category in ['concession','financing','supply','other']: m.transition(m.promoter,id,'submit',status=422);case.update({'editing':'saved/recovered','submission':'blocked-unapproved-module-profile','review':'not-eligible','publication':'blocked'}) m.persist();continue p=enrich(p,doc) if category=='investment_project': negative(id,p,'canonical.milestones.0.end_date',lambda q:q['milestones'][0].update({'end_date':'2026-01-01'})) negative(id,p,'canonical.milestones',lambda q:q['milestones'][0].update({'dependencies':[q['milestones'][1]['title']]})) negative(id,p,'canonical.financial_indicators',lambda q:q.update({'maturity_stage':'feasibility'})) p.update({'maturity_stage':'operational','financial_indicators':{'npv':-50,'irr':5,'payback_years':5,'scenario':'Cenário sintético negativo; sem score de sucesso','horizon_years':10,'currency':'MZN','discount_rate':6,'model_document_id':doc}}) p['evidences'].append({'title':'Estudo sintético sem validade real','evidence_type':'feasibility_study','document_id':doc}) elif category=='real_estate': negative(id,p,'canonical.real_estate.utilities',lambda q:q['real_estate'].update({'utilities':[{'type':'water','status':'existing'}]})) negative(id,p,'canonical.real_estate.title_document_id',lambda q:q['real_estate'].update({'title_document_id':str(uuid.uuid4())})) elif category=='land_rights': negative(id,p,'canonical.land_rights.community_consultation',lambda q:q['land_rights'].update({'communities_affected':True})) else: negative(id,p,'canonical.business_transaction.financial_periods',lambda q:q['business_transaction'].update({'financial_periods':q['business_transaction']['financial_periods'][:1]})) negative(id,p,'canonical.business_transaction.litigation_contingencies',lambda q:q['business_transaction'].update({'litigation_contingencies':[]})) m.save(m.promoter,id,p) recovered=m.page(m.Session('promoter').login(),id) assert recovered['values']['canonical']==m.normalized(p) location=recovered['canonicalOptions']['resources']['locations'][0]['payload'] geo=m.promoter.call('GET','/minhas-oportunidades/territory-geometries?territory_id='+location['district_id']) case['territorial_consumer']={'country':location['country_code'],'district_id':location['district_id'],'selected_geometry_available':geo['selected_geometry_available'],'areas_returned':len(geo['areas']),'rendered':False} for s in [m.promoter,m.validator,m.publisher]: raw=s.call('GET',f'/minhas-oportunidades/{id}/resources/documents/{doc}/download',binary=True) assert isinstance(raw,bytes) and raw.startswith(b'\x89PNG') m.sessions['outsider'].call('GET',f'/minhas-oportunidades/{id}/resources/documents/{doc}/download',status=403) m.transition(m.promoter,id,'submit');m.transition(m.validator,id,'assign_review',assignee_user_id=m.actors['validator']['id']) if category=='investment_project': m.transition(m.validator,id,'pending_opinion',gate='technical_review',recipient_user_id=m.actors['bootstrap']['id']) context=m.page(m.validator,id)['profileContext'];opinion=next(iter(context['opinions'])) assigned=reviewer.call('GET','/minhas-oportunidades?queue=assigned',inertia=True)['props']['records']['data'] assert any(r['id']==id for r in assigned) m.transition(m.validator,id,'approve',status=422) m.transition(m.validator,id,'respond_opinion',status=403,opinion_id=opinion,gate_decision='approved',evidence_document_ids=[doc]) m.transition(reviewer,id,'respond_opinion',opinion_id=opinion,gate_decision='approved',evidence_document_ids=[doc]) assert 'reviews' not in m.page(m.validator,id)['profileContext'] case['opinion']={'recipient_id':m.actors['bootstrap']['id'],'response_saved':True,'approval_while_pending':422,'wrong_recipient':403,'gate_not_automatically_approved':True} m.review(id,doc) if category=='land_rights': m.transition(m.publisher,id,'publish',status=422);case['publication']='blocked-pending-external-authorization' else: m.transition(m.publisher,id,'publish');case['publication']='published-internal' final=m.page(m.Session('promoter').login(),id) case.update({'editing':'saved/recovered','submission':'passed','review':'gates-approved','working_state':final['workflowState'],'document_recovered_allowed_profiles':[21,24,25],'foreign_organization_document_denied':403}) m.persist() proof['result']='passed';proof['completed_at']=datetime.now(timezone.utc).isoformat() print(json.dumps({'result':'passed','target':m.EXPECTED,'categories':len(proof['cases']),'negative_fields':len(proof['negative_fields']),'visual_pending':True,'active_writes':0})) except Exception: proof['result']='failed';raise finally: m.persist()