import json import unittest from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] CRM_PACK = REPO_ROOT / "salesforce-crm-standard" def load_json(path: Path): with path.open(encoding="utf-8") as handle: return json.load(handle) def object_spec(api_name: str): for spec in load_json(CRM_PACK / "objects" / "standard_objects_spec.json"): if spec.get("apiName") == api_name: return spec raise AssertionError(f"{api_name} object spec missing") def field_spec(spec: dict, api_name: str): for field in spec.get("fields", []): if field.get("apiName") == api_name: return field raise AssertionError(f"{spec.get('apiName')}.{api_name} field spec missing") def mapping_group(source: str, target: str, action_type: str): for group in load_json(CRM_PACK / "field_mappings" / "standard_field_mappings.json"): if ( group.get("sourceObject") == source and group.get("targetObject") == target and group.get("actionType") == action_type ): return group raise AssertionError(f"{source}->{target}/{action_type} mapping group missing") def mapping_pairs(group: dict): return { (mapping.get("sourceField"), mapping.get("targetField")) for mapping in group.get("mappings", []) } class CreateQuoteContractsTest(unittest.TestCase): def test_quote_record_type_field_does_not_require_synthetic_identity(self): quote = object_spec("Quote") record_type = field_spec(quote, "RecordTypeId") self.assertEqual(record_type.get("type"), "reference") self.assertEqual(record_type.get("referenceTo"), ["_System_RecordType"]) self.assertEqual(record_type.get("relationshipName"), "RecordType") record_types = load_json(CRM_PACK / "record_types" / "record_types.json")["recordTypes"] self.assertNotIn( "Quote", {group["objectApiName"] for group in record_types}, ) layout = load_json(CRM_PACK / "layouts" / "quote_page_layout.json") quote_info_fields = layout["sections"][0]["fields"] self.assertIn("RecordTypeId", quote_info_fields) def test_quote_line_item_declares_product_relationship_used_by_mapping(self): opportunity = object_spec("Opportunity") pricebook = field_spec(opportunity, "Pricebook2Id") currency = field_spec(opportunity, "CurrencyIsoCode") self.assertEqual(pricebook.get("type"), "reference") self.assertEqual(pricebook.get("referenceTo"), ["Pricebook2"]) self.assertEqual(currency.get("type"), "picklist") self.assertEqual(currency.get("default"), "USD") opportunity_line_item = object_spec("OpportunityLineItem") source_product = field_spec(opportunity_line_item, "Product2Id") self.assertEqual(source_product.get("type"), "reference") self.assertEqual(source_product.get("referenceTo"), ["Product2"]) self.assertEqual(source_product.get("relationshipName"), "Product2") quote_line_item = object_spec("QuoteLineItem") product = field_spec(quote_line_item, "Product2Id") self.assertEqual(product.get("type"), "reference") self.assertEqual(product.get("referenceTo"), ["Product2"]) self.assertEqual(product.get("relationshipName"), "Product2") def test_create_quote_field_mappings_cover_parent_and_line_items(self): quote_pairs = mapping_pairs(mapping_group("Opportunity", "Quote", "createQuote")) self.assertTrue( { ("Name", "Name"), ("AccountId", "AccountId"), ("Pricebook2Id", "Pricebook2Id"), ("CurrencyIsoCode", "CurrencyIsoCode"), ("Description", "Description"), }.issubset(quote_pairs) ) quote_line_pairs = mapping_pairs( mapping_group("OpportunityLineItem", "QuoteLineItem", "createQuote") ) self.assertTrue( { ("PricebookEntryId", "PricebookEntryId"), ("Product2Id", "Product2Id"), ("Quantity", "Quantity"), ("UnitPrice", "UnitPrice"), ("ListPrice", "ListPrice"), ("Discount", "Discount"), ("TotalPrice", "TotalPrice"), ("Description", "Description"), ("ServiceDate", "ServiceDate"), }.issubset(quote_line_pairs) ) def test_create_quote_flow_uses_matching_record_type_or_master(self): flow = load_json(CRM_PACK / "flows" / "Std_CreateQuoteFromOpportunity.flow.json") lookups = {lookup["name"]: lookup for lookup in flow["recordLookups"]} self.assertEqual(lookups["GetSourceOpportunityRecordType"]["object"], "_System_RecordType") self.assertEqual(lookups["GetQuoteObjectMetadata"]["object"], "_System_Object") self.assertEqual(lookups["GetMatchingQuoteRecordType"]["object"], "_System_RecordType") self.assertNotIn("GetDefaultQuoteRecordType", lookups) decisions = {decision["name"]: decision for decision in flow["decisions"]} self.assertNotIn("ChooseQuoteRecordType", decisions) self.assertNotIn("ValidateQuoteRecordTypeResolved", decisions) self.assertEqual( lookups["GetMatchingQuoteRecordType"]["connector"]["targetReference"], "GetOpportunityLineItems", ) custom_errors = { error["name"]: error["customErrorMessages"][0]["errorMessage"] for error in flow["customErrors"] } self.assertNotIn("FailMissingQuoteRecordType", custom_errors) quote_create = next( create for create in flow["recordCreates"] if create["name"] == "CreateQuote" ) assignments = { assignment["field"]: assignment["value"] for assignment in quote_create["inputAssignments"] } self.assertEqual(assignments["RecordTypeId"], {"elementReference": "$quoteRecordType.Id"}) def test_create_quote_action_grants_metadata_read_scope(self): actions = load_json(CRM_PACK / "actions" / "invocable_standard_actions.json") action = next(action for action in actions if action["name"] == "createQuote") scopes = { (scope["objectApiName"], scope["operation"]) for scope in action.get("internalAccessScopes", []) } self.assertIn(("_System_Object", "Read"), scopes) self.assertIn(("_System_RecordType", "Read"), scopes) if __name__ == "__main__": unittest.main()