class SIGNONINFO(Aggregate): """OFX section 7.2.2""" signonrealm = String(32, required=True) min = Integer(required=True) max = Integer(required=True) chartype = OneOf("ALPHAONLY", "NUMERICONLY", "ALPHAORNUMERIC", "ALPHAANDNUMERIC", required=True) casesen = Bool(required=True) special = Bool(required=True) spaces = Bool(required=True) pinch = Bool(required=True) chgpinfirst = Bool() usercred1label = String(64) usercred2label = String(64) clientuidreq = Bool() authtokenfirst = Bool() authtokenlabel = String(64) authtokeninfourl = String(255) mfachallengesupt = Bool() mfachallengefirst = Bool() accesstokenreq = Bool()
class XFERPROF(ElementList): """ OFX section 11.13.2.2 """ procdaysoff = ListElement(OneOf(*DAYS)) procendtm = Time(required=True) cansched = Bool(required=True) canrecur = Bool(required=True) canmodxfer = Bool(required=True) canmodmdls = Bool(required=True) modelwnd = Integer(3, required=True) dayswith = Integer(3, required=True) dfltdaystopay = Integer(3, required=True)
class XFERPROF(Aggregate): """ OFX section 11.13.2.2 """ # FIXME # Need to define an Aggregate subclass that support multiple repeated # Elements (not just SubAggregates, like List) for PROCDAYSOFF. procdaysoff = Unsupported() procendtm = Time(required=True) cansched = Bool(required=True) canrecur = Bool(required=True) canmodxfer = Bool(required=True) canmodmdls = Bool(required=True) modelwnd = Integer(3, required=True) dayswith = Integer(3, required=True) dfltdaystopay = Integer(3, required=True)
class CLOSUREOPT(INVTRAN, SECID): optaction = OneOf('EXERCISE', 'ASSIGN', 'EXPIRE') units = Decimal(required=True) shperctrct = Integer(required=True) subacctsec = OneOf(*INVSUBACCTS, required=True) relfitid = String(255) gain = Decimal()
class STMTTRN(Aggregate, Origcurrency): """ OFX section 11.4.3 """ trntype = OneOf(*TRNTYPES, required=True) dtposted = DateTime(required=True) dtuser = DateTime() dtavail = DateTime() trnamt = Decimal(required=True) fitid = String(255, required=True) correctfitid = String(255) correctaction = OneOf("REPLACE", "DELETE") srvrtid = String(10) checknum = String(12) refnum = String(32) sic = Integer() payeeid = String(12) name = String(32) payee = SubAggregate(PAYEE) extdname = String(100) bankacctto = SubAggregate(BANKACCTTO) ccacctto = SubAggregate(CCACCTTO) memo = String(255) imagedata = Unsupported() currency = SubAggregate(CURRENCY) origcurrency = SubAggregate(ORIGCURRENCY) inv401ksource = OneOf(*INV401KSOURCES) optionalMutexes = [ ("name", "payee"), ("ccacctto", "bankacctto"), ("currency", "origcurrency"), ]
class TAX1099MSGSETV1(Aggregate): """ OFX tax extensions section 2.1 """ msgsetcore = SubAggregate(MSGSETCORE, required=True) tax1099dnld = Bool(required=True) extd1099b = Bool(required=True) taxyearsupported = Integer(required=True)
class OPTINFO(SECINFO): opttype = OneOf('CALL', 'PUT', required=True) strikeprice = Decimal(required=True) dtexpire = DateTime(required=True) shperctrct = Integer(required=True) assetclass = OneOf(*ASSETCLASSES) fiassetclass = String(32) def __init__(self, elem): """ Strip SECID of underlying so it doesn't overwrite SECID of option during _flatten() """ # Do all XPath searches before removing nodes from the tree # which seems to mess up the DOM in Python3 and throw an # AttributeError on subsequent searches. secid = elem.find('./SECID') if secid is not None: # A <SECID> aggregate referring to the security underlying the # option is, in general, *not* going to be contained in <SECLIST> # (because you don't necessarily have a position in the underlying). # Since the <SECID> for the underlying only gives us fields for # (uniqueidtype, uniqueid) we can't really go ahead and use this # information to create a corresponding SECINFO instance (since we # lack information about the security subclass). It's unclear that # the SECID of the underlying is really needed for anything, so we # disregard it. elem.remove(secid) super(OPTINFO, self).__init__(elem)
class MSGSETCORE(Aggregate): """ OFX section 7.2.1 """ ver = Integer(required=True) url = String(255, required=True) ofxsec = OneOf("NONE", "TYPE1", required=True) transpsec = Bool(required=True) signonrealm = String(32, required=True) language = OneOf(*LANG_CODES, required=True) syncmode = OneOf("FULL", "LITE", required=True) refreshsupt = Bool() respfileer = Bool(required=True) spname = String(32) ofxextension = SubAggregate(OFXEXTENSION) @staticmethod def groom(elem): """ Remove proprietary tags e.g. INTU.XXX """ # Keep input free of side effects elem = deepcopy(elem) for child in set(elem): if "." in child.tag: elem.remove(child) return super(MSGSETCORE, MSGSETCORE).groom(elem)
class FIDIRECTDEPOSITINFO(Aggregate): """OFX tax extensions section 2.2.16""" finame_directdeposit = String(32, required=True) firoutingnum = Integer(9, required=True) fiacctnum = String(22, required=True) fiaccountnickname = String(32)
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 = ListAggregate(STTAXWHAGG) lcltaxwhagg = ListAggregate(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 SELLOPT(Aggregate): """ OFX section 13.9.2.4.4 """ invsell = SubAggregate(INVSELL, required=True) optselltype = OneOf(*OPTSELLTYPES, required=True) shperctrct = Integer(required=True) relfitid = String(255) reltype = OneOf('SPREAD', 'STRADDLE', 'NONE', 'OTHER') secured = OneOf('NAKED', 'COVERED')
class LOANINFO(Aggregate): """ OFX section 13.9.3 """ loanid = String(32, required=True) loandesc = String(32) initialloanbal = Decimal() loanstartdate = DateTime() currentloanbal = Decimal(required=True) dtasof = DateTime(required=True) loanrate = Decimal() loanpmtamt = Decimal() loanpmtfreq = OneOf(*LOANPMTFREQUENCIES) loanpmtsinitial = Integer(5) loanpmtsremaining = Integer(5) loanmaturitydate = DateTime() loantotalprojinterest = Decimal() loaninteresttodate = Decimal() loannextpmtdate = DateTime()
class INTERXFERMSGSETV1(Aggregate): """OFX section 11.13.4""" msgsetcore = SubAggregate(MSGSETCORE, required=True) xferprof = SubAggregate(XFERPROF, required=True) canbillpay = Bool(required=True) cancwnd = Integer(3, required=True) domxferfee = Decimal(required=True) intlxferfee = Decimal(required=True)
class SELLOPT(Aggregate): """ OFX section 13.9.2.4.4 """ invsell = SubAggregate(INVSELL, required=True) optselltype = OneOf(*OPTSELLTYPES, required=True) shperctrct = Integer(required=True) relfitid = String(255) reltype = OneOf("SPREAD", "STRADDLE", "NONE", "OTHER") secured = OneOf("NAKED", "COVERED")
class OPTINFO(Aggregate): """ OFX Section 13.8.5.4 """ secinfo = SubAggregate(SECINFO, required=True) opttype = OneOf('CALL', 'PUT', required=True) strikeprice = Decimal(required=True) dtexpire = DateTime(required=True) shperctrct = Integer(required=True) secid = SubAggregate(SECID) assetclass = OneOf(*ASSETCLASSES) fiassetclass = String(32)
class CLOSUREOPT(Aggregate): """ OFX section 13.9.2.4.4 """ invtran = SubAggregate(INVTRAN, required=True) secid = SubAggregate(SECID, required=True) optaction = OneOf('EXERCISE', 'ASSIGN', 'EXPIRE', required=True) units = Decimal(required=True) shperctrct = Integer(required=True) subacctsec = OneOf(*INVSUBACCTS, required=True) relfitid = String(255) gain = Decimal()
class BILLPAYMSGSETV1(Aggregate): """ OFX section 12.11.2 """ msgsetcore = SubAggregate(MSGSETCORE, required=True) dayswith = Integer(3, required=True) dfltdaystopay = Integer(3, required=True) xferdayswith = Integer(3, required=True) xferdfltdaystopay = Integer(3, required=True) # FIXME # Need to define an Aggregate subclass that support multiple repeated # Elements (not just SubAggregates, like List) for PROCDAYSOFF. procdaysoff = Unsupported() procendtm = Time(required=True) modelwnd = Integer(3, required=True) postprocwnd = Integer(3, required=True) stsviamods = Bool(required=True) pmtbyaddr = Bool(required=True) pmtbyxfer = Bool(required=True) pmtbypayeeid = Bool(required=True) canaddpayee = Bool(required=True) hasextdpmt = Bool(required=True) canmodpmts = Bool(required=True) canmodmdls = Bool(required=True) difffirstpmt = Bool(required=True) difflastpmt = Bool(required=True) billpubcontext = Bool()
class EXTDPMT(Aggregate): """ OFX Section 12.5.2.2 """ extdpmtfor = OneOf("INDIVIDUAL", "BUSINESS") extdpmtchk = Integer(10) extdpmtdsc = String(255) extdpmtinv = ListItem(EXTDPMTINV) @classmethod def validate_args(cls, *args, **kwargs): # "At least one of the following: <EXTDPMTDSC>, or <EXTDPMTINV>" listitems = [arg.__class__.__name__ for arg in args] if "EXTDPMTINV" not in listitems and "extdpmtdsc" not in kwargs: msg = "{} must contain at least one of [EXTDPMTDSC, EXTPMTINV]" raise ValueError(msg.format(cls.__name__)) super().validate_args(*args, **kwargs)
class TAX1099MSGSETV1(ElementList): """ OFX tax extensions section 2.1 """ msgsetcore = SubAggregate(MSGSETCORE, required=True) tax1099dnld = Bool(required=True) extd1099b = Bool(required=True) taxyearsupported = ListElement(Integer(4)) @classmethod def validate_args(cls, *args, **kwargs): # Must contain at least one TAXYEARSUPPORTED if len(args) == 0: msg = "{} must contain at least one of {}" raise ValueError(msg.format(cls.__name__, list(cls.listaggregates.keys()))) super().validate_args(*args, **kwargs)
class EXTDPAYEE(Aggregate): """ OFX Section 12.5.2.6 """ payeeid = String(12) idscope = OneOf("GLOBAL", "USER") # Required if <PAYEEID> is present. name = String(32) # Required if <PAYEEID> is present. daystopay = Integer(3, required=True) @classmethod def validate_args(cls, *args, **kwargs): # "If <PAYEEID> is present, <IDSCOPE> and <NAME> are required." payeeid = kwargs.get("payeeid", None) if payeeid: requiredGroup = ("idscope", "name") if not all(kwargs.get(attr, None) for attr in requiredGroup): msg = "{}(payeeid={}) must contain all of {}" raise ValueError(msg.format(cls.__name__, payeeid, requiredGroup)) super().validate_args(*args, **kwargs)
class TAX1099MISC_V100(Aggregate): """OFX tax extensions section 2.2.9""" srvrtid = String(10, required=True) taxyear = Integer(4, required=True) void = Bool() corrected = Bool() rents = Decimal() royalties = Decimal() otherincome = Decimal() federaltaxwh = Decimal() fishboatpro = Decimal() medhealthpay = Decimal() nonempcomp = Decimal() subpmts = Decimal() payer5ksales = Decimal() cropinspro = Decimal() sttaxwh = Decimal() payerstate = String(2) payerstid = String(32) stincome = Decimal() addlsttaxwhagg = ListAggregate(ADDLSTTAXWHAGG) grossattor = Decimal() excsgldn = Decimal() sec409adeferrals = Decimal() sec409aincome = Decimal() payeraddr = SubAggregate(PAYERADDR, required=True) payerid = String(32, required=True) recaddr = SubAggregate(RECADDR) recid = String(32) recacct = String(32) tinnot = Bool() fatca = Bool() optionalMutexes = [["sttaxwh", "addlsttaxwhagg"]] @classmethod def validate_args(cls, *args, **kwargs): if "STTAXWH" in kwargs and "PAYERSTATE" not in kwargs: msg = "{}: payerstate must also be provided if sttaxwh is provided" raise ValueError(msg) super().validate_args(*args, **kwargs)
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 = ListAggregate(FORINCOME) taxexemptint = Decimal() origstate = ListAggregate(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 TAX1099DIV_V100(Aggregate): """OFX tax extensions section 2.2.13""" srvrtid = String(10, required=True) taxyear = Integer(4, required=True) void = Bool() corrected = Bool() orddiv = Decimal() qualifieddiv = Decimal() totcapgain = Decimal() p28gain = Decimal() unrecsec1250 = Decimal() sec1202 = Decimal() nontaxdist = Decimal() fedtaxwh = Decimal() sec199a = Decimal() investexp = Decimal() fortaxpd = Decimal() forincomeamt = Decimal() forcnt = String(32) forincome = ListAggregate(FORINCOME) cashliq = Decimal() noncashliq = Decimal() exemptintdiv = Decimal() origstate = SubAggregate(ORIGSTATE) specifiedpabintdiv = Decimal() 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, required=True) recacct = String(32) tinnot = Bool() fatca = Bool() optionalMutexes = [["forcnt", "forincome"], ["statetaxwheld", "addlstatetaxwhagg"]]
class MSGSETCORE(ElementList): """ OFX section 7.2.1 """ ver = Integer(required=True) url = String(255, required=True) ofxsec = OneOf("NONE", "TYPE1", required=True) transpsec = Bool(required=True) signonrealm = String(32, required=True) language = ListElement(OneOf(*LANG_CODES)) syncmode = OneOf("FULL", "LITE", required=True) refreshsupt = Bool() respfileer = Bool(required=True) spname = String(32) ofxextension = SubAggregate(OFXEXTENSION) @classmethod def validate_args(cls, *args, **kwargs): if len(args) == 0: msg = "{} must contain at least one item" raise ValueError(msg.format(cls.__name__)) super().validate_args(*args, **kwargs)
class TAX1099OID_V100(Aggregate): """OFX tax extensions section 2.2.14""" srvrtid = String(10, required=True) taxyear = Integer(4, required=True) void = Bool() corrected = Bool() origisdisc = Decimal() otherperint = Decimal() erlwithpen = Decimal() fedtaxwh = Decimal() marketdiscount = Decimal() acqpremium = Decimal() description = String(2000) oidonustres = Decimal() investexp = Decimal() bondpremium = Decimal() taxexemptoid = Decimal() origstate = ListAggregate(ORIGSTATE) privactbondamt = Decimal() privactbondint = Decimal() statecode = String(2) stateidnum = String(32) statetaxwheld = Decimal() addlstatetaxwhagg = SubAggregate(ADDLSTATETAXWHAGG) payeraddr = SubAggregate(PAYERADDR) payerid = String(32, required=True) recid = String(32) recacct = String(32) tinnot = Bool() fatca = Bool() optionalMutexes = [ ["statecode", "addlstatetaxwhagg"], ["stateidnum", "addlstatetaxwhagg"], ["statetaxwheld", "addlstatetaxwhagg"], ]
class STMTTRN(TRAN, ORIGCURRENCY): trntype = OneOf('CREDIT', 'DEBIT', 'INT', 'DIV', 'FEE', 'SRVCHG', 'DEP', 'ATM', 'POS', 'XFER', 'CHECK', 'PAYMENT', 'CASH', 'DIRECTDEP', 'DIRECTDEBIT', 'REPEATPMT', 'OTHER', required=True) dtposted = DateTime(required=True) dtuser = DateTime() dtavail = DateTime() trnamt = Decimal(required=True) correctfitid = Decimal() correctaction = OneOf('REPLACE', 'DELETE') checknum = String(12) refnum = String(32) sic = Integer() payeeid = String(12) name = String(32) memo = String(255) inv401ksource = OneOf(*INV401KSOURCES) payee = None bankacctto = None ccacctto = None
class MSGSETCORE(Aggregate): """ OFX section 7.2.1 """ ver = Integer(required=True) url = String(255, required=True) ofxsec = OneOf('NONE', 'TYPE1', required=True) transpsec = Bool(required=True) signonrealm = String(32, required=True) language = OneOf(*LANG_CODES, required=True) syncmode = OneOf('FULL', 'LITE', required=True) refreshsupt = Bool() respfileer = Bool(required=True) spname = String(32) ofxextension = SubAggregate(OFXEXTENSION) @staticmethod def groom(elem): """ Remove proprietary tags e.g. INTU.XXX """ for child in set(elem): if '.' in child.tag: elem.remove(child) return super(MSGSETCORE, MSGSETCORE).groom(elem)
class TAX1099B_V100(Aggregate): """OFX tax extensions section 2.2.11""" srvrtid = String(10, required=True) taxyear = Integer(4, required=True) void = Bool() corrected = Bool() dtsale = DateTime() cusipnum = String(32) stkbnd = SubAggregate(STKBND) bartering = Decimal() fedtaxwh = Decimal() dscr = String(80) profit = Decimal() unrelprofitprev = Decimal() unrelprofit = Decimal() aggprofit = Decimal() extdbinfo_v100 = SubAggregate(EXTDBINFO_V100) payeraddr = SubAggregate(PAYERADDR, required=True) payerid = String(32, required=True) recaddr = SubAggregate(RECADDR) recid = String(32, required=True) recacct = String(32) tinnot = Bool()
class BILLPAYMSGSETV1(ElementList): """ OFX section 12.11.2 """ msgsetcore = SubAggregate(MSGSETCORE, required=True) dayswith = Integer(3, required=True) dfltdaystopay = Integer(3, required=True) xferdayswith = Integer(3, required=True) xferdfltdaystopay = Integer(3, required=True) procdaysoff = ListElement(OneOf(*DAYS)) procendtm = Time(required=True) modelwnd = Integer(3, required=True) postprocwnd = Integer(3, required=True) stsviamods = Bool(required=True) pmtbyaddr = Bool(required=True) pmtbyxfer = Bool(required=True) pmtbypayeeid = Bool(required=True) canaddpayee = Bool(required=True) hasextdpmt = Bool(required=True) canmodpmts = Bool(required=True) canmodmdls = Bool(required=True) difffirstpmt = Bool(required=True) difflastpmt = Bool(required=True) billpubcontext = Bool()
class RECURRINST(Aggregate): """ OFX section 10.2 """ ninsts = Integer(3) freq = OneOf(*FREQUENCIES, required=True)