class SIGNUPMSGSRSV1(Aggregate): """ OFX section 8.1 """ enrolltrnrs = ListItem(ENROLLTRNRS) acctinfotrnrs = ListItem(ACCTINFOTRNRS) accttrnrs = ListItem(ACCTTRNRS) chguserinfotrnrs = ListItem(CHGUSERINFOTRNRS)
class BANKMSGSRQV1(Aggregate): """ OFX section 11.13.1.1.1 """ stmttrnrq = ListItem(STMTTRNRQ) stmtendtrnrq = ListItem(STMTENDTRNRQ) stpchktrnrq = ListItem(STPCHKTRNRQ) intratrnrq = ListItem(INTRATRNRQ) recintratrnrq = ListItem(RECINTRATRNRQ) bankmailtrnrq = ListItem(BANKMAILTRNRQ) stpchksyncrq = ListItem(STPCHKSYNCRQ) intrasyncrq = ListItem(INTRASYNCRQ) recintrasyncrq = ListItem(RECINTRASYNCRQ) bankmailsyncrq = ListItem(BANKMAILSYNCRQ) @property def statements(self): stmts = [] for trnrq in self: stmtrq = None if isinstance(trnrq, STMTTRNRQ): stmtrq = trnrq.stmtrq elif isinstance(trnrq, STMTTRNRQ): stmtrq = trnrq.stmtendrq if stmtrq is not None: stmts.append(stmtrq) return stmts
class INTERXFERMSGSRSV1(Aggregate): """ OFX section 11.13.1.3.2 """ intertrnrs = ListItem(INTERTRNRS) recintertrnrs = ListItem(RECINTERTRNRS) intersyncrs = ListItem(INTERSYNCRS) recintersyncrs = ListItem(RECINTERSYNCRS)
class INTERXFERMSGSRQV1(Aggregate): """ OFX section 11.13.1.3.1 """ intertrnrq = ListItem(INTERTRNRQ) recintertrnrq = ListItem(RECINTERTRNRQ) intersyncrq = ListItem(INTERSYNCRQ) recintersyncrq = ListItem(RECINTERSYNCRQ)
class BANKMSGSRSV1(Aggregate): """ OFX section 11.13.1.1.2 """ stmttrnrs = ListItem(STMTTRNRS) stmtendtrnrs = ListItem(STMTENDTRNRS) stpchktrnrs = ListItem(STPCHKTRNRS) intratrnrs = ListItem(INTRATRNRS) recintratrnrs = ListItem(RECINTRATRNRS) bankmailtrnrs = ListItem(BANKMAILTRNRS) stpchksyncrs = ListItem(STPCHKSYNCRS) intrasyncrs = ListItem(INTRASYNCRS) recintrasyncrs = ListItem(RECINTRASYNCRS) bankmailsyncrs = ListItem(BANKMAILSYNCRS) @property def statements(self): stmts = [] for trnrs in self: stmtrs = None if isinstance(trnrs, STMTTRNRS): stmtrs = trnrs.stmtrs elif isinstance(trnrs, STMTENDTRNRS): stmtrs = trnrs.stmtendrs if stmtrs is not None: # Staple wrapper TRNUID, CLTCOOKIE onto STMTRS for convenience stmtrs.trnuid = trnrs.trnuid stmtrs.cltcookie = trnrs.cltcookie stmts.append(stmtrs) return stmts
class INVPOSLIST(Aggregate): """ OFX section 13.9.2.2 """ posdebt = ListItem(POSDEBT) posmf = ListItem(POSMF) posopt = ListItem(POSOPT) posother = ListItem(POSOTHER) posstock = ListItem(POSSTOCK)
class SECLIST(Aggregate): """ OFX section 13.8.4.4 """ debtinfo = ListItem(DEBTINFO) mfinfo = ListItem(MFINFO) optinfo = ListItem(OPTINFO) otherinfo = ListItem(OTHERINFO) stockinfo = ListItem(STOCKINFO)
class CREDITCARDMSGSRSV1(Aggregate): """ OFX section 11.13.1.1.2 """ ccstmttrnrs = ListItem(CCSTMTTRNRS) ccstmtendtrnrs = ListItem(CCSTMTTRNRS) @property def statements(self): return [trnrs.statement for trnrs in self]
class BANKMSGSRQV1(Aggregate): """ OFX section 11.13.1.1.1 """ stmttrnrq = ListItem(STMTTRNRQ) stmtendtrnrq = ListItem(STMTENDTRNRQ) stpchktrnrq = ListItem(STPCHKTRNRQ) intratrnrq = ListItem(INTRATRNRQ) recintratrnrq = ListItem(RECINTRATRNRQ) bankmailtrnrq = ListItem(BANKMAILTRNRQ) stpchksyncrq = ListItem(STPCHKSYNCRQ) intrasyncrq = ListItem(INTRASYNCRQ) recintrasyncrq = ListItem(RECINTRASYNCRQ) bankmailsyncrq = ListItem(BANKMAILSYNCRQ)
class ACCTINFO(Aggregate): """ OFX section 8.5.3 The text description is a little ambiguous. Here's what the DTD says: <xsd:sequence> <xsd:element name="DESC" type="ofx:ShortMessageType" minOccurs="0"/> <xsd:element name="PHONE" type="ofx:PhoneType" minOccurs="0"/> <xsd:sequence maxOccurs="unbounded"> <xsd:choice> <xsd:element name="BANKACCTINFO" type="ofx:BankAccountInfo"/> <xsd:element name="CCACCTINFO" type="ofx:CreditCardAccountInfo"/> <xsd:element name="BPACCTINFO" type="ofx:BillPaymentAccountInfo"/> <xsd:element name="INVACCTINFO" type="ofx:InvestmentAccountInfo"/> <xsd:element name="PRESACCTINFO" type="ofx:PresentmentAccountInfo"/> </xsd:choice> </xsd:sequence> </xsd:sequence> """ desc = String(80) phone = String(32) bankacctinfo = ListItem(BANKACCTINFO) ccacctinfo = ListItem(CCACCTINFO) bpacctinfo = ListItem(BPACCTINFO) invacctinfo = ListItem(INVACCTINFO) # presacctinfo = ListItem(PRESACCTINFO) @classmethod def validate_args(cls, *args, **kwargs): # Must contain at least one <xxxACCTINFO> if len(args) == 0: msg = "{} must contain at least one of {}" raise ValueError(msg.format(cls.__name__, cls.listitems.keys())) # For a given service xxx, there can be at most one <xxxACCTINFO> # returned. For example, you cannot return two <BANKACCTINFO> # aggregates. sortKey = operator.attrgetter("__class__.__name__") args_copy = sorted(args, key=sortKey) for tag, group in itertools.groupby(args_copy, key=sortKey): if len(list(group)) > 1: msg = "{} contains multiple {} aggregates" raise ValueError(msg.format(cls.__name__, tag)) super().validate_args(*args, **kwargs) def __repr__(self): return "<{} desc='{}' phone='{}' len={}>".format( self.__class__.__name__, self.desc, self.phone, len(self) )
class BILLPAYMSGSRSV1(Aggregate): """ OFX section 12.11.1.2 """ pmttrnrs = ListItem(PMTTRNRS) recpmttrnrs = ListItem(RECPMTTRNRS) payeetrnrs = ListItem(PAYEETRNRS) pmtinqtrnrs = ListItem(PMTINQTRNRS) pmtmailtrns = ListItem(PMTMAILTRNRS) pmtsyncrs = ListItem(PMTSYNCRS) recpmtsyncrs = ListItem(RECPMTSYNCRS) payeesyncrs = ListItem(PAYEESYNCRS) pmtmailsyncrs = ListItem(PMTMAILSYNCRS)
class INVMAILSYNCRQ(SyncRqList): """ OFX Section 13.10.2.1 """ incimages = Bool(required=True) usehtml = Bool(required=True) invacctfrom = SubAggregate(INVACCTFROM) invmailtrnrq = ListItem(INVMAILTRNRQ)
class INV401K(Aggregate): """ OFX section 13.9.3 """ employername = String(32, required=True) planid = String(32) planjoindate = DateTime() employercontactinfo = String(255) brokercontactinfo = String(255) deferpctpretax = Decimal() deferpctaftertax = Decimal() matchinfo = SubAggregate(MATCHINFO) contribinfo = SubAggregate(CONTRIBINFO) currentvestpct = Decimal() vestinfo = ListItem(VESTINFO) loaninfo = ListItem(LOANINFO) inv401ksummary = SubAggregate(INV401KSUMMARY)
class INVSTMTMSGSRSV1(Aggregate): """ OFX section 13.7.1.2.2 """ invstmttrnrs = ListItem(INVSTMTTRNRS) invmailtrnrs = ListItem(INVMAILTRNRS) invmailsyncrs = ListItem(INVMAILSYNCRS) @property def statements(self): stmts = [] for rs in self: if isinstance(rs, INVSTMTTRNRS): stmtrs = rs.invstmtrs if stmtrs is not None: stmts.append(stmtrs) return stmts
class INVSTMTMSGSRQV1(Aggregate): """ OFX section 13.7.1.2.1 """ invstmttrnrq = ListItem(INVSTMTTRNRQ) invmailtrnrq = ListItem(INVMAILTRNRQ) invmailsyncrq = ListItem(INVMAILSYNCRQ) @property def statements(self): stmts = [] for trnrq in self: if isinstance(trnrq, INVSTMTTRNRQ): stmtrq = trnrq.invstmtrq if stmtrq is not None: stmts.append(stmtrq) return stmts
class TAX1099R_V100(Aggregate): """ OFX tax extensions section 2.2.10 """ srvrtid = String(10, required=True) taxyear = Integer(4, required=True) void = Bool() grossdist = Bool() taxamt = Decimal() taxamtnd = Decimal() totaldist = Bool() capgain = Decimal() fedtaxwh = Decimal() empcontins = Decimal() netunapmp = Decimal() nonempcomp = Decimal() distcode = String(1, required=True) irasepsimp = Bool() annctrctdist = Decimal() annctrctper = Decimal() pertodist = Decimal() totempcont = Decimal() amtallocableirr = Decimal() firstyeardesigroth = Integer(4) sttaxwhagg = ListItem(STTAXWHAGG) lcltaxwhagg = ListItem(LCLTAXWHAGG) payeraddr = SubAggregate(PAYERADDR, required=True) payerid = String(32, required=True) recaddr = SubAggregate(RECADDR) recid = String(32) recacct = String(32) fatca = Bool() dtbenefitpmt = DateTime() @classmethod def validate_args(cls, *args, **kwargs): # "[IRASEPSIMP] is required if any of the following tags are present in # the 1099R aggregate: GROSSDIST, TAXAMT, FEDTAXWH, STTAXWH, # or LCLTAXWH" has_irasepsimp = "irasepsimp" in kwargs for tag in ("grossdist", "taxamt", "fedtaxwh", "sttaxwh", "lcltaxwh"): if tag in kwargs and not has_irasepsimp: msg = ( "{}.__init__(): irasepsimp must also be provided if {} is provided" ) raise ValueError(msg.format(cls.__name__, tag)) super().validate_args(*args, **kwargs)
class INTERSYNCRS(SyncRsList): """ OFX section 11.12.3.2 """ bankacctfrom = SubAggregate(BANKACCTFROM) ccacctfrom = SubAggregate(CCACCTFROM) intertrnrs = ListItem(INTERTRNRS) requiredMutexes = [("bankacctfrom", "ccacctfrom")]
class STPCHKRS(Aggregate): """ OFX section 11.6.1.1 """ curdef = OneOf(*CURRENCY_CODES, required=True) bankacctfrom = SubAggregate(BANKACCTFROM, required=True) stpchknum = ListItem(STPCHKNUM) fee = Decimal(required=True) feemsg = String(80, required=True)
class BANKMAILSYNCRS(SyncRsList): """ OFX section 11.12.7.2 """ bankacctfrom = SubAggregate(BANKACCTFROM) ccacctfrom = SubAggregate(CCACCTFROM) bankmailtrnrs = ListItem(BANKMAILTRNRS) requiredMutexes = [("bankacctfrom", "ccacctfrom")]
class RECINTRASYNCRS(SyncRsList): """ OFX section 11.12.5.2 """ bankacctfrom = SubAggregate(BANKACCTFROM) ccacctfrom = SubAggregate(CCACCTFROM) recintratrnrs = ListItem(RECINTRATRNRS) requiredMutexes = [("bankacctfrom", "ccacctfrom")]
class SECLISTMSGSRSV1(Aggregate): """ OFX section 13.7.2.2.2 """ # N.B. this part of the spec is unusual in that SECLIST is a direct # child of SECLISTMSGSRSV1, unwrapped. SECLISTRS, wrapped in SECLISTTRNS, # is an empty aggregate; including SECLISTTRNRS/SECLISTRS under # SECLISTMSGSTSV1 merely indicates that the accompanying SECLIST was # generated in response to a client SECLISTRQ. seclisttrnrs = ListItem(SECLISTTRNRS) seclist = ListItem(SECLIST) @property def securities(self): securities = [] for child in self: if isinstance(child, SECLIST): securities.extend(child) return securities
class INVSTMTMSGSRSV1(Aggregate): """ OFX section 13.7.1.2.2 """ invstmttrnrs = ListItem(INVSTMTTRNRS) invmailtrnrs = ListItem(INVMAILTRNRS) invmailsyncrs = ListItem(INVMAILSYNCRS) @property def statements(self): stmts = [] for trnrs in self: if isinstance(trnrs, INVSTMTTRNRS): stmtrs = trnrs.invstmtrs if stmtrs is not None: # Staple wrapper TRNUID, CLTCOOKIE onto STMTRS for convenience stmtrs.trnuid = trnrs.trnuid stmtrs.cltcookie = trnrs.cltcookie stmts.append(stmtrs) return stmts
class CREDITCARDMSGSRQV1(Aggregate): """ OFX section 11.13.1.1.1 """ ccstmttrnrq = ListItem(CCSTMTTRNRQ) ccstmtendtrnrq = ListItem(CCSTMTENDTRNRQ) @property def statements(self): stmts = [] for trnrq in self: stmtrq = None if isinstance(trnrq, CCSTMTTRNRQ): stmtrq = trnrq.ccstmtrq elif isinstance(trnrq, CCSTMTENDTRNRQ): stmtrq = trnrq.ccstmtendrq if stmtrq is not None: stmts.append(stmtrq) return stmts
class INVOICE(Aggregate): """ OFX Section 12.5.2.3 """ invno = String(32, required=True) invtotalamt = Decimal(required=True) invpaidamt = Decimal(required=True) invdate = DateTime(required=True) invdesc = String(80, required=True) discount = SubAggregate(DISCOUNT) adjustment = SubAggregate(ADJUSTMENT) lineitem = ListItem(LINEITEM)
class ACCTINFORS(Aggregate): """ OFX section 8.5.2 """ dtacctup = DateTime(required=True) acctinfo = ListItem(ACCTINFO) def __repr__(self): return "<{} dtacctup='{}' len={}>".format( self.__class__.__name__, self.dtacctup, len(self) )
class INTERSYNCRQ(SyncRqList): """ OFX section 11.12.3.1 """ bankacctfrom = SubAggregate(BANKACCTFROM) ccacctfrom = SubAggregate(CCACCTFROM) intertrnrq = ListItem(INTERTRNRQ) requiredMutexes = SyncRqList.requiredMutexes + [ ("bankacctfrom", "ccacctfrom") ]
class RECINTRASYNCRQ(SyncRqList): """ OFX section 11.12.5.1 """ bankacctfrom = SubAggregate(BANKACCTFROM) ccacctfrom = SubAggregate(CCACCTFROM) recintratrnrq = ListItem(RECINTRATRNRQ) requiredMutexes = SyncRqList.requiredMutexes + [ ("bankacctfrom", "ccacctfrom") ]
class TAX1099INT_V100(Aggregate): """ OFX tax extensions section 2.2.12 """ srvrtid = String(10, required=True) taxyear = Integer(4, required=True) void = Bool() corrected = Bool() payerrtn = String(32) intincome = Decimal() intusbndtrs = Decimal() fedtaxwh = Decimal() investexp = Decimal() fortaxpd = Decimal() forincomeamt = Decimal() forcnt = String(32) forincome = ListItem(FORINCOME) taxexemptint = Decimal() origstate = ListItem(ORIGSTATE) specifiedpabint = Decimal() marketdiscount = Decimal() bondpremium = Decimal() bondpremusobligations = Decimal() tebondpremium = Decimal() cusipnum = String(32) statecode = String(2) stateidnum = String(32) statetaxwheld = Decimal() addlstatetaxwhagg = SubAggregate(ADDLSTATETAXWHAGG) payeraddr = SubAggregate(PAYERADDR, required=True) payerid = String(32, required=True) recaddr = SubAggregate(RECADDR) recid = String(32) recacct = String(32) tinnot = Bool() fatca = Bool() optionalMutexes = [ ["forcnt", "forincome"], ["statecode", "addlstatetaxwhagg"], ["stateidnum", "addlstatetaxwhagg"], ["statetaxwheld", "addlstatetaxwhagg"], ]
class BANKMAILSYNCRQ(SyncRqList): """ OFX section 11.12.7.1 """ incimages = Bool(required=True) usehtml = Bool(required=True) bankacctfrom = SubAggregate(BANKACCTFROM) ccacctfrom = SubAggregate(CCACCTFROM) bankmailtrnrq = ListItem(BANKMAILTRNRQ) requiredMutexes = SyncRqList.requiredMutexes + [ ("bankacctfrom", "ccacctfrom") ]
class CONTRIBINFO(Aggregate): """ OFX section 13.9.3 """ contribsecurity = ListItem(CONTRIBSECURITY) @classmethod def validate_args(cls, *args, **kwargs): # "current contribution allocation for a security (1 or more)" if len(args) == 0: msg = "{} must contain at least one item" raise ValueError(msg.format(cls.__name__)) super().validate_args(*args, **kwargs)